为Swift并发和响应式编程提供完整原语集
- 1.4.0是最新版本,但仅适用于Swift 4.2和5.0
- 使用1.3.0适用于Swift 4.0+
- 使用1.2.4适用于Swift 3的最新版本
特性 | |
---|---|
强大的原语 |
Future ,Promise ,Channel ,Producer ,Sink ,Cache ,... |
通用的转换 |
map ,filter ,recover ,debounce ,distinct ,... |
方便的组合 |
flatMap ,merge ,zip ,sample ,scan ,reduce ,... |
改进现有功能 |
键值观察、target-action、通知、绑定 |
更少的模板代码 |
整洁的取消、线程、内存管理 |
可扩展 |
强大扩展适用于URLSession 、UI控件、CoreData 、... |
所有平台 |
|
文档 |
100% + 示例代码,查看完整文档 |
简单集成 |
SPM,CocoaPods,Carthage |
通讯
响应式编程
响应式属性
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> {
/* ... */
}
}
- “不要忘记”批注 无
- 只要指定的上下文(MyViewController)存在,代码块就会保留并调用
- 想看更详细的解释吗?
使用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
}
}