AsyncNinja 1.4.0

AsyncNinja 1.4.0

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最新版本发布2019年3月
SPM支持SPM

Anton Mironov维护。



AsyncNinja Title

为Swift并发和响应式编程提供完整原语集

Gitter CocoaPods Carthage compatible Build Status

  • 1.4.0是最新版本,但仅适用于Swift 4.2和5.0
  • 使用1.3.0适用于Swift 4.0+
  • 使用1.2.4适用于Swift 3的最新版本
特性
🦄
强大的原语
FuturePromiseChannelProducerSinkCache,...
🤘
通用的转换
mapfilterrecoverdebouncedistinct,...
✌️
方便的组合
flatMapmergezipsamplescanreduce,...
🙌
改进现有功能
键值观察、target-action、通知、绑定
🍳
更少的模板代码
整洁的取消、线程、内存管理
🕶
可扩展
强大扩展适用于URLSession、UI控件、CoreData、...
🍱
所有平台
🖥macOS 10.10+📱iOS 8.0+📺tvOS 9.0+⌚️watchOS 2.0+🐧Linux
🤓
文档
100% + 示例代码,查看完整文档
🔩
简单集成
SPMCocoaPodsCarthage

通讯

响应式编程

响应式属性

let searchResults = searchBar.rp.text
  .debounce(interval: 0.3)
  .distinct()
  .flatMap(behavior: .keepLatestTransform) { (query) -> Future<[SearchResult]> in
    return query.isEmpty
      ? .just([])
      : searchGitHub(query: query).recover([])
  }

绑定

  • 自动解绑
  • 自动派发到正确的队列
  • 无需 .observeOn(MainScheduler.instance) 且无需 .disposed(by: disposeBag)
class MyViewController: UIViewController {
  /* ... */
  @IBOutlet weak var myLabel: UILabel!

  override func viewDidLoad() {
    super.viewDidLoad()
    UIDevice.current.rp.orientation
      .map { $0.description }
      .bind(myLabel.rp.text)
  }
  
  /* ... */
}

上下文使用

  • 无需 [weak self]
  • 无需 DispatchQueue.main.async { ... }
  • 无需 .observeOn(MainScheduler.instance)
class MyViewController: NSViewController {
  let service: MyService

  /* ... */
  
  func fetchAndPresentItems(for request: Request) {
    service.perform(request: request)
      .map(context: self, executor: .primary) { (self, response) in
        return self.items(from: response)
      }
      .onSuccess(context: self) { (self, items) in
        self.present(items: items)
      }
      .onFailure(context: self) { (self, error) in
        self.present(error: error)
      }
  }
  
  func items(from response: Response) throws -> [Items] {
    /* ... extract items from response ... */
  }
  
  func present(items: [Items]) {
    /* ... update UI ... */
  }
}

class MyService {
  func perform(request: Request) -> Future<Response> {
    /* ... */
  }
}

深入探讨

假设我们有

  • Person 是包含个人信息的一个示例结构。
  • MyService 是一个作为模型入口点的类的示例,其在后台运行。
  • MyViewController 是一个管理与 UI 相关实例的类的示例,其在主线程上运行。

回调中的代码

extension MyViewController {
  func present(personWithID identifier: String) {
    myService.fetch(personWithID: identifier) {
      (person, error) in

      /* do not forget to dispatch to the main queue */
      DispatchQueue.main.async {

        /* do not forget the [weak self] */
        [weak self] in
        guard let strongSelf = self
          else { return }

        if let person = person {
          strongSelf.present(person: person)
        } else if let error = error {
          strongSelf.present(error: error)
        } else {
          fatalError("There is neither person nor error. What has happened to this world?")
        }
      }
    }
  }
}

extension MyService {
  func fetch(personWithID: String, callback: @escaping (Person?, Error?) -> Void) {
    /* ... */
  }
}
  • 不要忘记"不要忘记"批注 2倍
  • 即使MyViewController已经被销毁,代码块仍然会被保留和调用

使用提供Versatile的库的代码

extension MyViewController {
  func present(personWithID identifier: String) {
    myService.fetch(personWithID: identifier)

      /* do not forget to dispatch to the main queue */
      .onComplete(executor: .main) {

        /* do not forget the [weak self] */
        [weak self] (completion) in
        if let strongSelf = self {
          completion.onSuccess(strongSelf.present(person:))
          completion.onFailure(strongSelf.present(error:))
        }
      }
  }
}

extension MyService {
  func fetch(personWithID: String) -> Future<Person> {
    /* ... */
  }
}
  • 不要忘记"不要忘记"批注 2倍
  • 即使MyViewController已经被销毁,代码块仍然会被保留和调用

使用AsyncNinja

extension MyViewController {
  func present(personWithID identifier: String) {
    myService.fetch(personWithID: identifier)
      .onSuccess(context: self) { (self, person) in
        self.present(person: person)
      }
      .onFailure(context: self) { (self, error) in
        self.present(error: error)
      }
  }
}

extension MyService {
  func fetch(personWithID: String) -> Future<Person> {
    /* ... */
  }
}

使用Futures

假设我们有一个函数用于查找小于n的所有素数

func primeNumbers(to n: Int) -> [Int] { /* ... */ }

创建future

let futurePrimeNumbers: Future<[Int]> = future { primeNumbers(to: 10_000_000) }

应用转换

let futureSquaredPrimeNumbers = futurePrimeNumbers
  .map { (primeNumbers) -> [Int] in
    return primeNumbers.map { (number) -> Int
      return number * number
    }
  }

同步等待完成

if let fallibleNumbers = futurePrimeNumbers.wait(seconds: 1.0) {
  print("Number of prime numbers is \(fallibleNumbers.success?.count)")
} else {
  print("Did not calculate prime numbers yet")
}

订阅完成

futurePrimeNumbers.onComplete { (falliblePrimeNumbers) in
  print("Number of prime numbers is \(falliblePrimeNumbers.success?.count)")
}

组合未来

let futureA: Future<A> = /* ... */
let futureB: Future<B> = /* ... */
let futureC: Future<C> = /* ... */
let futureABC: Future<(A, B, C)> = zip(futureA, futureB, futureC)

从基于回调的流程过渡到基于未来的流程

class MyService {
  /* implementation */
  
  func fetchPerson(withID personID: Person.Identifier) -> Future<Person> {
    let promise = Promise<Person>()
    self.fetchPerson(withID: personID, callback: promise.complete)
    return promise
  }
}

从基于未来的流程过渡到基于回调的流程

class MyService {
  /* implementation */
  
  func fetchPerson(withID personID: Person.Identifier,
                   callback: @escaping (Fallible<Person>) -> Void) {
    self.fetchPerson(withID: personID)
      .onComplete(callback)
  }
}

使用通道

假设我们有一个返回质数通道的函数:当找到质数时发送质数,以找到的数目作为完成信号

func makeChannelOfPrimeNumbers(to n: Int) -> Channel<Int, Int> { /* ... */ }

应用转换

let channelOfSquaredPrimeNumbers = channelOfPrimeNumbers
  .map { (number) -> Int in
      return number * number
    }

同步迭代更新值。

for number in channelOfPrimeNumbers {
  print(number)
}

同步等待完成

if let fallibleNumberOfPrimes = channelOfPrimeNumbers.wait(seconds: 1.0) {
  print("Number of prime numbers is \(fallibleNumberOfPrimes.success)")
} else {
  print("Did not calculate prime numbers yet")
}

同步等待完成 #2

let (primeNumbers, numberOfPrimeNumbers) = channelOfPrimeNumbers.waitForAll()

订阅更新

channelOfPrimeNumbers.onUpdate { print("Update: \($0)") }

订阅完成

channelOfPrimeNumbers.onComplete { print("Completed: \($0)") }

创建 Channel

func makeChannelOfPrimeNumbers(to n: Int) -> Channel<Int, Int> {
  return channel { (update) -> Int in
    var numberOfPrimeNumbers = 0
    var isPrime = Array(repeating: true, count: n)

    for number in 2..<n where isPrime[number] {
      numberOfPrimeNumbers += 1
      update(number)

      // updating seive
      var seiveNumber = number + number
      while seiveNumber < n {
        isPrime[seiveNumber] = false
        seiveNumber += number
      }
    }

    return numberOfPrimeNumbers
  }
}