RxSwift I:开始学习 RxSwift
📒RxSwift I:开始学习 RxSwift
第一章:RxSwift 介绍
RxSwift是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。
RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。
介绍异步编程
使用 Cocoa 和 UIKit 异步的API的问题在于:复杂的异步代码变得非常难写,部分原因是苹果SDK提供的API种类繁多。如NotificationCenter、Delegate、GCD、闭包、Combine。
异步编程词汇表:
- 状态(State),具体地说:共享可变状态
- 命令式编程(Imperative programming)
- 副作用(Side effects)
- 声明式编程(Declarative code):声明式代码让您可以定义行为片段。只要有相关事件发生,RxSwift 就会运行这些行为,并提供一个不可变的、孤立的数据片段来处理。这样,您可以使用异步代码,但做出与简单 for 循环相同的假设:您正在使用不可变数据并且可以以顺序、确定的方式执行代码。
- 响应式系统(Reactive systems)
响应式系统(Reactive systems)是一个相当抽象的术语,它涵盖了Web或iOS应用程序,它们显示了大多数或全部以下特性:
- 响应式设计(Responsive):始终保持UI更新,代表了最新的应用程序状态。
- 能复原的(Resilient):每个行为都是独立定义的,并提供灵活的错误恢复。
- 灵活的(Elastic): 该代码处理不同的工作负载,通常实现诸如懒惰驱动数据收集、事件节流和资源共享等特性。
- 消息驱动(Message driven):组件使用基于消息的通信来提高可重用性和隔离性,解耦类的生命周期和实现。
RxSwift 基础
这个标志是一只电鳗。(Rx 项目曾经被称为Volta)
RxSwift 是微软开源的 ReactiveX 的Swift语言的实现。
RxSwift 在传统的Cocoa编程和纯函数编程之间找到了最佳位置。它允许您对事件作出反应, 方法是使用不可变的代码定义以确定性的、可组合的方式处理异步输入部分。
Rx代码的三个组成部分是 Observable
, Operator
和 Scheduler
。
Observable<Element>
类提供了Rx代码的基础:异步产生一系列事件的能力,它可以“携带”数据的不可变快照。简单来说,它允许类在一段时间内订阅其他类发出的值。
ObservableType
协议 (Observable
需要遵循的) 非常简单。Observable
可能发出 (并且Observer
能接受) 仅三类型事件:
- next 下一个事件: “携带” 最新 (或 “下一个 “) 数据值的事件。这是Observer
“接收” 值的方式。
- completed 已完成的事件: 此事件以成功终止事件序列。这意味着Observable
完成其生命周期成功, 不会发出任何其他事件。
- error 错误事件: Observable
终止带有错误, 不会发出其他事件.
两种不同的可观测序列: 有限和无限的。
由于它们是高度解耦和可组合的, 所以这些方法通常称为Operator
。比如filter。
Operator
也是高度可组合的,它们总是把数据作为输入并输出它们的结果,所以你可以用许多不同的方式轻松地将它们连接起来,实现比单个Operator
自己能做的更多的事情。
Scheduler
是GCD和OperationQueue的Rx等价物。
RxSwift将充当你的订阅(在左边)和Scheduler
(在右边)之间的调度器,将工件发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。
要读取此关系图, 请在不同的计划程序中按预定的顺序 (1、2、3、…) 来执行彩色作品。例如:
·蓝色网络订阅以在基于自定义 NSOperation 的计划程序上运行的一段代码 (1) 开始。
·数据输出块作为下一个块 (2) 的输入, 它运行在一个不同的调度程序上, 它位于并发后台 GCD 队列中。
·最后, 在主线程调度程序上计划最后一块蓝色代码 (3), 以便用新数据更新 UI。
App architecture 应用的架构
值得一提的是,RxSwift并没有以任何方式改变应用程序的架构;它主要处理事件、异步数据序列和通用通信协议。
通过在苹果开发文档中实现MVC体系结构,可以创建具有Rx的应用程序。如果你喜欢的话,你也可以选择实现MVP架构或MVVM。RxSwift也可以帮你实现自己的单向数据架构。
微软的MVVM架构是专门针对在平台上创建的事件驱动软件开发的,该平台提供数据绑定。RxSwift和MVVM很好地结合在一起,在这本书的末尾,你会看到这个模式以及如何用RxSwift来实现它。
MVVM和RxSwift结合在一起的原因是,ViewModel允许您公开Observable
属性,这些属性可以直接绑定到View Controller 代码中的UIKit控件。这使得绑定模型数据到UI非常简单地展示和编码:
本书中的所有其他示例都使用MVC架构来保持示例代码简单易懂。
RxCocoa
RxSwift是通用Rx API的实现。因此,它不涉及任何Cocoa或UIKit类。
RxCocoa是RxSwift的配套库,所有的类都有助于UIKit和Cocoa的开发。除了具有一些高级类之外,RxCocoa还为许多UI组件添加了响应式扩展,以便您可以订阅不同的UI事件。
例如,使用RxCocoa订阅UISwitch的状态变化是非常容易的,例如:
1 | toggleSwitch.rx.isOn |
RxCocoa adds the rx.isOn property (among others) to the UISwitch class so you can subscribe to generally useful event sequences.
RxCocoa将rx.isOn属性(其中之一)添加到UISwitch类,这样您就可以订阅通常有用的Observable
序列。
安装
官方git:https://github.com/ReactiveX/RxSwift
使用 CocoaPods 、 Carthage 和 Swift Package Manager 均很方便集成RxSwift。
RxSwift 和 Combine
RxSwift 和 Combine(以及 Swift 中的其他相应式编程框架)共享许多通用语言和非常相似的概念。
RxSwift是一个较旧的,完善的框架,具有一些自己的原始概念,运算符名称和类型多样性,这主要是由于其多平台跨语言标准,该标准也适用于Linux,这对于Server-Side Swift非常有用。它也是开源的,所以如果你愿意,你可以直接为其核心做出贡献,并确切地看到它的特定部分是如何工作的。它与所有支持 Swift 的 Apple 平台版本兼容,一直支持 iOS 8。
Combine 是 Apple 新的、闪亮的框架,涵盖了类似的概念,但专门针对 Swift 和 Apple 自己的平台量身定制。它与 Swift 标准库共享许多通用语言,因此即使新手也觉得 API 非常熟悉。它仅支持从iOS 13,macOS 10.15等开始的较新的Apple平台。不幸的是,截至今天,它还没有开源,并且不支持Linux。
幸运的是,由于 RxSwift 和 Combine 非常相似,因此您的 RxSwift 知识可以轻松转移到 Combine,反之亦然。RxCombine(https://github.com/CombineCommunity/RxCombine)等项目允许您根据需要混合搭配 RxSwift Observables 和 Combine Publishers。
RxSwift 6.5.0 也迎来了 Swift Concurrency 的支持。提供了互操作性:
await
调用Observable
的values
- 封装
async
Task 为Observable
社区
RxSwift社区非常友好,思想开放,并且热衷于讨论模式,常用技巧或互相帮助。
更多的Rx库和实验,像雨后春笋一样的涌现,可以在这里找到:https://github.com/RxSwiftCommunity
可能最好的方式来满足许多对RxSwift感兴趣的人,这是Slack的频道:http://rxswift-slack.herokuapp.com。
Slack频道拥有约5000名成员! 日常的主题包括:互相帮助,讨论RxSwift或其同伴库的潜在新功能,以及共享RxSwift博客文章和会议讲座。
第二章:Observables
Observable 是什么
Observable、observable sequence 和 sequence 在 Rx 都是一个意思。或者在其他Rx实现中称之为stream。
最好称之为 Observable,不过翻译过来还是序列顺口些。
Observable 的生命周期
- observable发出包含元素的next事件。 它可以继续这样做,直到它:
- …发出error事件并终止,或
- …发出completed事件并终止。
- 一旦observable被终止,它不能再发出事件。
将这种概念化的最佳方法之一是使用弹珠图(Marble Diagrams 基于时间轴上绘制的值)。
创建 Observable
类型擦除的ObservableType,也就是 Swift 中的泛型。
它代表了一种推式风格队列。
let observable: Observable
订阅 Observable
订阅observable sequence的事件处理程序。
func subscribe(_ on: @escaping (RxSwift.Event<Self.E>) -> Swift.Void) -> Disposable
1 | let one = 1 |
在指定的范围内生成一个整数的observable sequence,使用指定的scheduler生成和发送Observer
消息。
1 | static func range(start: Self.E, count: Self.E, scheduler: ImmediateSchedulerType = default) -> RxSwift.Observable<Self.E> |
1 | let observable = Observable<Int>.range(start: 1, count: 10) |
Disposing 和 terminating
请记住, 在收到订阅之前, Observable的内容不会执行任何事情。它是触发一个Observable的开始发出事件的订阅, 直到它发出. error 或.completed完成事件并终止。您可以通过取消对它的订阅来手动终止Observable。
subscription.dispose()
单独管理每个订阅将是单调乏味的, 因此 RxSwift 引入 DisposeBag 类型。DisposeBag 持有 disposable协议的对象,通常是使用.disposed(by:)
方法, 并将调用dispose(), 当DisposeBag即将deallocated。
1 | let observable = Observable.of("A", "B", "C") |
create操作符接受一个名为subscribe的参数。 它的工作是提供对可观察对象进行调用订阅的实现。 换句话说,它定义了将发送给订阅者的所有事件。
1 | static func create(_ subscribe: @escaping (AnyObserver<String>) -> Disposable) -> Observable<String> |
创建 observable 工厂类
可以创建一个可观察的工厂,向每个订阅者发布一个新的Observable,而不是创建一个等待订阅者的Observable。
1 | static func deferred(_ observableFactory: @escaping () throws -> Observable<Int>) -> Observable<Int> |
1 | let disposeBag = DisposeBag() |
1 | 123 |
使用Traits
Traits是具有比常规Observable更窄的行为集合的一种Observable。它们的使用是可选的;您可以在任何可能使用trait的地方使用常规Observable。他们的目的是提供一种给你的API或者代码的读者更清楚地表达意图的方式。
RxSwift中有三种Traits: Single, Maybe 和 Completable.
Singles将发出.success(value)或.error事件。 .success(value)实际上是.next和.completed事件的组合。 这对于一次性进程非常有用,它可以成功并产生一个值或失败,例如下载数据或从磁盘加载数据。
下面的例子是读取 Copyright.txt
的文件内容:
1 | let disposeBag = DisposeBag() |
副作用
do
Operator允许插入副作用,处理程序执行过程中并不会以任何方式更改发出的事件的操作。
打印debug 信息
debug
Operator, 它将打印observable的每个事件的信息。
第三章:Subjects
Subject 是什么
既可以作为Observable
,也可以作为Observer
,这就是所谓的 Subjects。
也是最方便互操作的,
BehaviorRelay
比较常用。
RxSwift 中有四个subject类型:
PublishSubject
: 开始为空, 只向订阅者发出新元素.ReplaySubject
: 用缓冲区大小, 并将保持元素的缓冲区大小, 并将其重播到新订阅者.BehaviorSubject
: 从初始值开始, 将其重播或将最新的元素给新订阅者.AsyncSubject
:仅发出序列中的最后一个next事件,并且仅当subject收到completed事件时才发出。这是一个很少使用的主题。
PublishSubject
1 | /// Represents an object that is both an observable sequence as well as an observer. |
当你只是想让订阅者只接受在订阅的时候以后发生的新的事件,直到他们unsubscribe,或者subject已经terminated以.completed或.error事件的方式,PublishSubject就可以派上用场。
第一个订阅者在将 1 添加到subject后进行订阅,因此它不会收到该事件。不过,它确实得到了2和3。由于第二个订阅者在添加2之前不会加入,因此它只能获得3。
1 | extension ObservableType { |
1 | // 从订阅开始向所有观察者广播新事件。 |
1 | --- PublishSubject example --- |
ReplaySubject
1 | /// Represents an object that is both an observable sequence as well as an observer. |
ReplaySubject将临时缓存, 或缓冲区, 它们发出的最新元素, 由您选择的 specified 大小决定。然后, 他们会将该缓冲区重播到新订阅者。
1 | // 向所有观察者广播新事件,并向新观察者广播之前指定的bufferSize大小的事件数。 |
1 | --- ReplaySubject example --- |
BehaviorSubject
1 | /// Represents a value that changes over time. |
当您希望使用最新数据预先填充View时, BehaviorSubject非常有用。例如, 可以将用户详情页中的控件绑定到BehaviorSubject, 以便在应用程序获取新数据时, 可以使用最新值来预先填充显示。
1 | example("BehaviorSubject") { |
1 | --- BehaviorSubject example --- |
Relay
Relay在保持其replay行为的同时包装了subject。与其他subject不同,可以使用 accept(_:)
添加值,而非 onNext(_:)
。这是因为Relay只能接受值,即不能向它们添加错误或已完成的事件。
PublishRelay将包装PublishSubject ,而BehaviorRelay将包装BehaviorSubject。中继与包装主体的区别在于,它们可以保证永远不会终止。
1 | import RxRelay |
1 | --- PublishRelay example --- |
1 | import RxRelay |
1 | --- BehaviorRelay example --- |
第四章:Observables 和 Subjects 实践
本章重点是在一个完整的应用开发中使用RxSwift,一步一步的学会如何把概念应用到实际项目中。
在 view controller 中使用 subject/relay
1 | override func viewDidLoad() { |
使用 subject 在 view controller 之间传值
view controller 之间传值可以通过 delegate,但是用 subject 更好。
1 | @IBAction func actionAdd() { |
封装已有API为Observable
1 | import Foundation |
RxSwift traits 实践
Single
Single 只有.success
或 .error
两种事件 。适合作为封装网络接口的返回值,要么成功,要么失败。
1 | PhotoWriter.save(image) |
Maybe
Maybe 有三种事件:.success
、.completed
和 .error
。
和 Single 一样,你既可以通过 Maybe.create({ ... })
直接创建,也可以使用 .asMaybe()
1 | PhotoWriter.save(image) |
Completable
Completable 只有.completed
和 .error
两种事件。适合只关心是否完成,而不需要传递value的情况。使用方法:Completable.create({ ... })
1 | class PhotoWriter { |