內(nèi)容概覽:
- Rx Marble Diagrams(寶石圖)
- 關(guān)鍵概念
- Event - 事件
- Observable - 產(chǎn)生事件
- Observer - 響應(yīng)事件
- Operator - 創(chuàng)建變化組合事件
- Disposable - 管理綁定(訂閱)的生命周期
- Schedulers - 線程隊(duì)列調(diào)配
為什么要使用Rx(ReactiveX)?
- 簡(jiǎn)化異步操作
- 簡(jiǎn)化多線程操作
- 更簡(jiǎn)潔的代碼
- 多平臺(tái)適用(C++, Java, C#, JavaScript, Python,Scala,Kotlin,Go)
另外,官方文檔解釋了一個(gè)很重要的點(diǎn):ReactiveX不是函數(shù)響應(yīng)式編程(Functional Reactive Programming)!
請(qǐng)不要再被人忽悠,也不要用這個(gè)概念去忽悠別人~
Rx Marble Diagrams(寶石圖)

Observable發(fā)出事件,然后被Observer接收并被轉(zhuǎn)換為其他形式,轉(zhuǎn)換后的事件又繼續(xù)被發(fā)出。
當(dāng)Observable序列完成時(shí),或者出現(xiàn)錯(cuò)誤時(shí),序列被終結(jié)。
Observable是一個(gè)序列,類(lèi)似于Swift中的序列,但是這個(gè)序列可以異步接收元素。
Observable的subscribe方法,類(lèi)似于Swift序列中的makeIterator方法。
Observer(通常是一系列的閉包)需要傳遞給Observable的subscribe,以接收序列的元素。
Rx抽象了時(shí)間狀態(tài)機(jī),我們可以更方便地去完成業(yè)務(wù)邏輯。
否則,我們就要重復(fù)地去編寫(xiě)一些有關(guān)臨時(shí)狀態(tài)轉(zhuǎn)換的邏輯代碼。
寶石圖可以幫助你理解Rx中的操作符
一個(gè)關(guān)于數(shù)字的序列:
--1--2--3--4--5--6--| // 序列完成,并終結(jié)
另一個(gè)關(guān)于字符的序列
--a--b--a--a--a---d---X // 序列發(fā)生錯(cuò)誤,并終結(jié)
有些序列是無(wú)窮無(wú)盡的,比如按鈕點(diǎn)擊事件序列:
---tap-tap-------tap--->
這里有一條基本的規(guī)則:
- 序列可以產(chǎn)生0個(gè)或多個(gè)元素。
- 只要序列完成了或者發(fā)生了錯(cuò)誤,序列就不能再產(chǎn)生元素。
關(guān)鍵概念
- Event - 事件
- Observable - 產(chǎn)生事件
- Observer - 響應(yīng)事件
- Operator - 創(chuàng)建變化組合事件
- Disposable - 管理綁定(訂閱)的生命周期
- Schedulers - 線程隊(duì)列調(diào)配
Event - 事件
/// 代表一系列事件
public enum Event<Element> {
/// 序列產(chǎn)生了一個(gè)新的元素
case next(Element)
/// 創(chuàng)建序列時(shí)產(chǎn)生了一個(gè)錯(cuò)誤,導(dǎo)致序列終止
case error(Swift.Error)
/// 序列的所有元素都已經(jīng)成功產(chǎn)生,整個(gè)序列已經(jīng)完成
case completed
}
請(qǐng)注意,序列產(chǎn)生了錯(cuò)誤,就會(huì)導(dǎo)致序列終止!
有時(shí)候,這并不是你期待的結(jié)果?。?!
Observable - 產(chǎn)生事件
創(chuàng)建Observable,并讓Observer訂閱事件,在事件結(jié)束后釋放觀察者。
typealias JSON = Any
let disposeBag = DisposeBag()
let json: Observable<JSON> = Observable.create { (observer) -> Disposable in
let task = URLSession.shared.dataTask(with: ...) { data, _, error in
guard error == nil else {
observer.onError(error!)
return
}
guard let data = data,
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
else {
observer.onError(DataError.cantParseJSON)
return
}
observer.onNext(jsonObject)
observer.onCompleted()
}
task.resume()
return Disposables.create { task.cancel() }
}
json.subscribe(onNext: { json in
print("取得 json 成功: \(json)")
}, onError: { error in
print("取得 json 失敗 Error: \(error.localizedDescription)")
}, onCompleted: {
print("取得 json 任務(wù)成功完成")
}).disposed(by: disposeBag) // 釋放Observers
Observable 的冷與熱
一創(chuàng)建就可以開(kāi)始發(fā)出事件的,就是熱的。
直到有observer訂閱才開(kāi)始發(fā)出事件的,就是冷的!
Observable 特征序列(定制版的Observable)
-
Single
要么發(fā)出一個(gè)元素,要么產(chǎn)生一個(gè) error 事件。
一個(gè)比較常見(jiàn)的例子就是執(zhí)行 HTTP 請(qǐng)求,然后返回一個(gè)應(yīng)答或錯(cuò)誤。
不過(guò)你也可以用 Single 來(lái)描述任何只有一個(gè)元素的序列。
示例Demo:
let disposeBag = DisposeBag()
func getRepo(_ repo: String) -> Single<[String: Any]> {
return Single<[String: Any]>.create { single in
let url = URL(string: "https://api.github.com/repos/\(repo)")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
single(.error(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
let result = json as? [String: Any] else {
single(.error(DataError.cantParseJSON))
return
}
single(.success(result))
}
task.resume()
return Disposables.create { task.cancel() }
}
}
getRepo("ReactiveX/RxSwift").subscribe(onSuccess: { json in
print("JSON: ", json)
}, onError: { error in
print("Error: ", error)
}).disposed(by: disposeBag)
訂閱提供一個(gè) SingleEvent 的枚舉:
public enum SingleEvent<Element> {
case success(Element)
case error(Swift.Error)
}
-
Completable
要么產(chǎn)生一個(gè) completed 事件,要么產(chǎn)生一個(gè) error 事件。
Completable 適用于那種你只關(guān)心任務(wù)是否完成,而不需要在意任務(wù)返回值的情況。
示例Demo:
let disposeBag = DisposeBag()
func cacheLocally() -> Completable {
return Completable.create { completable in
// Store some data locally
...
...
guard success else {
completable(.error(CacheError.failedCaching))
return Disposables.create {}
}
completable(.completed)
return Disposables.create {}
}
}
cacheLocally().subscribe(onCompleted: {
print("Completed with no error")
}, onError: { error in
print("Completed with an error: \(error.localizedDescription)")
}).disposed(by: disposeBag)
訂閱提供一個(gè) CompletableEvent 的枚舉:
public enum CompletableEvent {
case error(Swift.Error)
case completed
}
-
Maybe
它介于 Single 和 Completable 之間,它要么發(fā)出一個(gè)元素,要么產(chǎn)生一個(gè)completed事件,要么產(chǎn)生一個(gè)error事件。
如果你遇到那種可能需要發(fā)出一個(gè)元素,又可能不需要發(fā)出時(shí),就可以使用 Maybe。
示例Demo:
let disposeBag = DisposeBag()
func generateString() -> Maybe<String> {
return Maybe<String>.create { maybe in
maybe(.success("RxSwift"))
// OR
maybe(.completed)
// OR
maybe(.error(error))
return Disposables.create {}
}
}
generateString().subscribe(onSuccess: { element in
print("Completed with element \(element)")
}, onError: { error in
print("Completed with an error \(error.localizedDescription)")
}, onCompleted: {
print("Completed with no element")
}).disposed(by: disposeBag)
訂閱提供一個(gè) MaybeEvent 的枚舉:
public enum MaybeEvent<Element> {
case success(Element)
case error(Swift.Error)
case completed
}
-
Driver
為UI元素精心準(zhǔn)備的特征序列- 不會(huì)產(chǎn)生 error 事件
- 一定在 MainScheduler 監(jiān)聽(tīng)(主線程監(jiān)聽(tīng))
- 共享狀態(tài)變化
-
ControlEvent
用于描述 UI 控件所產(chǎn)生的事件(如:按鈕點(diǎn)擊事件、輸入框文本更新事件等)。- 不會(huì)產(chǎn)生 error 事件
- 一定在 MainScheduler 訂閱(主線程訂閱)
- 一定在 MainScheduler 監(jiān)聽(tīng)(主線程監(jiān)聽(tīng))
- 共享狀態(tài)變化
Observer - 響應(yīng)事件
button.rx.tap.subscribe(onNext: { [weak self] in
self?.showAlert()
}, onError: { error in
print("發(fā)生錯(cuò)誤: \(error.localizedDescription)")
}, onCompleted: {
print("任務(wù)完成")
})
創(chuàng)建觀察者最直接的方法就是在 Observable 的 subscribe 方法后面描述事件發(fā)生時(shí),需要如何做出響應(yīng)。
而觀察者就是由后面的 onNext,onError,onCompleted的這些閉包構(gòu)建出來(lái)的。
特征觀察者(定制的Observer)
-
AnyObserver
可以用來(lái)描敘任意一種觀察者。
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(onNext: { data in
print("Data Task Success with count: \(data.count)")
}, onError: { error in
print("Data Task Error: \(error)")
})
.disposed(by: disposeBag)
以上代碼可以看作:
let observer: AnyObserver<Data> = AnyObserver { (event) in
switch event {
case .next(let data):
print("Data Task Success with count: \(data.count)")
case .error(let error):
print("Data Task Error: \(error)")
default:
break
}
}
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(observer)
.disposed(by: disposeBag)
let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
switch event {
case .next(let isHidden):
self?.usernameValidOutlet.isHidden = isHidden
default:
break
}
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
以上代碼如果用Binder實(shí)現(xiàn),效果更佳。
let observer: Binder<Bool> = Binder(usernameValidOutlet) { (view, isHidden) in
view.isHidden = isHidden
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
Binder 可以只處理 next 事件,并且保證響應(yīng) next 事件的代碼一定會(huì)在給定 Scheduler 上執(zhí)行,這里采用默認(rèn)的 MainScheduler。
RxCocoa中已經(jīng)采用Binder實(shí)現(xiàn)了很多常用的觀察者,比如:
extension Reactive where Base: UIView {
public var isHidden: Binder<Bool> {
// self.base是實(shí)際的UI控件對(duì)象
return Binder(self.base) { view, hidden in
view.isHidden = hidden
}
}
}
extension Reactive where Base: UIControl {
public var isEnabled: Binder<Bool> {
return Binder(self.base) { control, value in
control.isEnabled = value
}
}
}
extension Reactive where Base: UILabel {
public var text: Binder<String?> {
return Binder(self.base) { label, text in
label.text = text
}
}
}
你也可以用這種方式來(lái)創(chuàng)建自定義的 UI 觀察者。
Observable & Observer
有些事物比較特別。它們既是可被監(jiān)聽(tīng)的序列也是觀察者。
有許多 UI 控件都存在這種特性,例如:switch的開(kāi)關(guān)狀態(tài),segmentedControl的選中索引號(hào),datePicker的選中日期等等。
// 作為可被監(jiān)聽(tīng)的序列
let observable = textField.rx.text
observable.subscribe(onNext: { text in show(text: text) })
// 作為觀察者
let observer = textField.rx.text
let text: Observable<String?> = ...
text.bind(to: observer)
框架里面定義了一些輔助類(lèi)型可以幫助你更準(zhǔn)確的描述事物的特征,它們既是可被監(jiān)聽(tīng)的序列也是觀察者。
AsyncSubject
將在源 Observable 產(chǎn)生完成事件后,發(fā)出最后一個(gè)元素(僅僅只有最后一個(gè)元素)。PublishSubject
發(fā)送訂閱后產(chǎn)生的元素,而在訂閱前發(fā)出的元素將不會(huì)發(fā)送給觀察者。ReplaySubject
發(fā)送全部的元素(元素個(gè)數(shù)由buffer的大小決定),無(wú)論觀察者是何時(shí)進(jìn)行訂閱的。BehaviorSubject
當(dāng)觀察者對(duì) BehaviorSubject 進(jìn)行訂閱時(shí),它會(huì)將源 Observable 中最新的元素發(fā)送出來(lái)(如果不存在最新的元素,就發(fā)出默認(rèn)元素)。然后將隨后產(chǎn)生的元素發(fā)送出來(lái)。Variable
在 Swift 中我們經(jīng)常會(huì)用 var 關(guān)鍵字來(lái)聲明變量。
RxSwift 提供的 Variable 實(shí)際上是 var 的 Rx 版本,你可以將它看作是 RxVar。
如果我們聲明的變量需要提供 Rx 支持,那就選用 Variable 這個(gè)類(lèi)型。-
ControlProperty
ControlProperty 專(zhuān)門(mén)用于描述 UI 控件屬性的,它具有以下特征:- 不會(huì)產(chǎn)生 error 事件
- 一定在 MainScheduler 訂閱(主線程訂閱)
- 一定在 MainScheduler 監(jiān)聽(tīng)(主線程監(jiān)聽(tīng))
- 共享狀態(tài)變化
Operator - 創(chuàng)建變化組合事件
操作符可以幫助大家創(chuàng)建新的序列,或者變化組合原有的序列,從而生成一個(gè)新的序列。
filter - 過(guò)濾
// 溫度
let rxTemperature: Observable<Double> = ...
// filter 操作符
rxTemperature.filter { temperature in temperature > 33 }
.subscribe(onNext: { temperature in
print("高溫:\(temperature)度")
})
.disposed(by: disposeBag)
map - 轉(zhuǎn)換
// JSON
let json: Observable<JSON> = ...
// map 操作符
json.map(Model.init)
.subscribe(onNext: { model in
print("取得 Model: \(model)")
})
.disposed(by: disposeBag)
zip - 配對(duì)
// 漢堡
let rxHamburg: Observable<Hamburg> = ...
// 薯?xiàng)l
let rxFrenchFries: Observable<FrenchFries> = ...
// zip 操作符
Observable.zip(rxHamburg, rxFrenchFries)
.subscribe(onNext: { (hamburg, frenchFries) in
print("取得漢堡: \(hamburg) 和薯?xiàng)l:\(frenchFries)")
})
.disposed(by: disposeBag)
Rx提供了充分的操作符來(lái)幫我們創(chuàng)建序列。當(dāng)然如果內(nèi)置操作符無(wú)法滿足你的需求時(shí),你還可以創(chuàng)建自定義的操作符。
如果你不確定該如何選擇操作符,可以參考 決策樹(shù)。它會(huì)引導(dǎo)你找出合適的操作符。
Disposable - 管理綁定(訂閱)的生命周期
通常來(lái)說(shuō),一個(gè)序列如果發(fā)出了 error 或者 completed 事件,那么所有內(nèi)部資源都會(huì)被釋放。
如果你需要提前釋放這些資源或取消訂閱的話,那么你可以對(duì)返回的 可被清除的資源(Disposable) 調(diào)用 dispose 方法。
但是,推薦使用 清除包(DisposeBag) 或者 takeUntil 操作符 來(lái)管理訂閱的生命周期。
DisposeBag用法:
var disposeBag = DisposeBag()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textField.rx.text.orEmpty
.subscribe(onNext: { text in print(text) })
.disposed(by: self.disposeBag)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.disposeBag = DisposeBag()
}
takeUntil 用法:
override func viewDidLoad() {
super.viewDidLoad()
_ = usernameValid
.takeUntil(self.rx.deallocated)
.bind(to: passwordOutlet.rx.isEnabled)
_ = usernameValid
.takeUntil(self.rx.deallocated)
.bind(to: usernameValidOutlet.rx.isHidden)
_ = passwordValid
.takeUntil(self.rx.deallocated)
.bind(to: passwordValidOutlet.rx.isHidden)
_ = everythingValid
.takeUntil(self.rx.deallocated)
.bind(to: doSomethingOutlet.rx.isEnabled)
_ = doSomethingOutlet.rx.tap
.takeUntil(self.rx.deallocated)
.subscribe(onNext: { [weak self] in self?.showAlert() })
}
Schedulers - 線程隊(duì)列調(diào)配
Schedulers 是 Rx 實(shí)現(xiàn)多線程的核心模塊,它主要用于控制任務(wù)在哪個(gè)線程或隊(duì)列運(yùn)行。

一個(gè)比較典型的例子就是,在后臺(tái)發(fā)起網(wǎng)絡(luò)請(qǐng)求,然后解析數(shù)據(jù),最后在主線程刷新頁(yè)面。你就可以先用 subscribeOn 切到后臺(tái)去發(fā)送請(qǐng)求并解析數(shù)據(jù),最后用 observeOn 切換到主線程更新頁(yè)面。
GCD實(shí)現(xiàn):
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
self.data = data
}
}
RxSwift實(shí)現(xiàn):
let rxData: Observable<Data> = ...
rxData
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] data in
self?.data = data
})
.disposed(by: disposeBag)
MainScheduler
MainScheduler 代表主線程。
如果你需要執(zhí)行一些和 UI 相關(guān)的任務(wù),就需要切換到該 Scheduler 運(yùn)行。SerialDispatchQueueScheduler
SerialDispatchQueueScheduler 抽象了串行 DispatchQueue。
如果你需要執(zhí)行一些串行任務(wù),可以切換到這個(gè) Scheduler 運(yùn)行。ConcurrentDispatchQueueScheduler
ConcurrentDispatchQueueScheduler 抽象了并行 DispatchQueue。
如果你需要執(zhí)行一些并發(fā)任務(wù),可以切換到這個(gè) Scheduler 運(yùn)行。OperationQueueScheduler
OperationQueueScheduler 抽象了 NSOperationQueue。
它具備 NSOperationQueue 的一些特點(diǎn),例如,你可以通過(guò)設(shè)置 maxConcurrentOperationCount,來(lái)控制同時(shí)執(zhí)行并發(fā)任務(wù)的最大數(shù)量。
Error Handling - 錯(cuò)誤處理
一旦序列里面產(chǎn)出了一個(gè) error 事件,整個(gè)序列將被終止。RxSwift 主要有兩種錯(cuò)誤處理機(jī)制:
- retry - 重試
- catch - 恢復(fù)
retry - 重試
請(qǐng)求 JSON 失敗時(shí),立即重試,重試 3 次后仍然失敗就將錯(cuò)誤拋出:
let rxJson: Observable<JSON> = ...
rxJson
.retry(3)
.subscribe(onNext: { json in
print("取得 JSON 成功: \(json)")
}, onError: { error in
print("取得 JSON 失敗: \(error)")
})
.disposed(by: disposeBag)
請(qǐng)求 JSON 失敗時(shí),等待 5 秒后重試:
let retryDelay: Double = 5 // 重試延時(shí) 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return Observable.timer(retryDelay, scheduler: MainScheduler.instance)
}
.subscribe(...)
.disposed(by: disposeBag)
retryWhen 操作符主要描述應(yīng)該在何時(shí)重試,并且通過(guò)閉包里面返回的 Observable 來(lái)控制重試的時(shí)機(jī)。
如果重試超過(guò) 4 次,就將錯(cuò)誤拋出。如果錯(cuò)誤在 4 次以內(nèi)時(shí),就等待 5 秒后重試:
let maxRetryCount = 4 // 最多重試 4 次
let retryDelay: Double = 5 // 重試延時(shí) 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in
guard index < maxRetryCount else {
return Observable.error(error)
}
return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance)
}
}
.subscribe(...)
.disposed(by: disposeBag)
flatMapWithIndex 操作符可以提供錯(cuò)誤的索引數(shù) index。然后用這個(gè)索引數(shù)判斷是否超過(guò)最大重試數(shù),如果超過(guò)了,就將錯(cuò)誤拋出。如果沒(méi)有超過(guò),就等待 5 秒后重試。
catch - 恢復(fù)
catchError 可以在錯(cuò)誤產(chǎn)生時(shí),用一個(gè)備用元素或者一組備用元素將錯(cuò)誤替換掉。
當(dāng)錯(cuò)誤產(chǎn)生時(shí),就返回一個(gè)空數(shù)組,于是就會(huì)顯示一個(gè)空列表頁(yè):
searchBar.rx.text.orEmpty
...
.flatMapLatest { query -> Observable<[Repository]> in
...
return searchGitHub(query)
.catchErrorJustReturn([])
}
...
.bind(to: ...)
.disposed(by: disposeBag)
你也可以使用 catchError,當(dāng)錯(cuò)誤產(chǎn)生時(shí),將錯(cuò)誤事件替換成一個(gè)備選序列:
// 先從網(wǎng)絡(luò)獲取數(shù)據(jù),如果獲取失敗了,就從本地緩存獲取數(shù)據(jù)
let rxData: Observable<Data> = ... // 網(wǎng)絡(luò)請(qǐng)求的數(shù)據(jù)
let cahcedData: Observable<Data> = ... // 之前本地緩存的數(shù)據(jù)
rxData
.catchError { _ in cahcedData }
.subscribe(onNext: { date in
print("獲取數(shù)據(jù)成功: \(date.count)")
})
.disposed(by: disposeBag)
Result
如果我們只是想給用戶錯(cuò)誤提示,那要如何操作呢?
以下提供一個(gè)最為直接的方案,不過(guò)這個(gè)方案存在一些問(wèn)題:
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Void> in
return update(userInfo)
}
.observeOn(MainScheduler.instance)
.subscribe(onNext: {
print("用戶信息更新成功")
}, onError: { error in
print("用戶信息更新失?。?\(error.localizedDescription)")
})
.disposed(by: disposeBag)
這樣實(shí)現(xiàn)是非常直接的。但是 一旦網(wǎng)絡(luò)請(qǐng)求操作失敗了,序列就會(huì)終止??!整個(gè)訂閱將被取消??!
如果用戶再次點(diǎn)擊更新按鈕,就無(wú)法再次發(fā)起網(wǎng)絡(luò)請(qǐng)求進(jìn)行更新操作了。
為了解決這個(gè)問(wèn)題,我們需要選擇合適的方案來(lái)進(jìn)行錯(cuò)誤處理。例如使用枚舉 Result:
// 自定義一個(gè)枚舉類(lèi)型 Result
public enum Result<T> {
case success(T)
case failure(Swift.Error)
}
然后之前的代碼需要修改成:
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Result<Void>> in
return update(userInfo)
.map(Result.success) // 轉(zhuǎn)換成 Result
.catchError { error in Observable.just(Result.failure(error)) }
}
.observeOn(MainScheduler.instance)
.subscribe(onNext: { result in
switch result { // 處理 Result
case .success:
print("用戶信息更新成功")
case .failure(let error):
print("用戶信息更新失?。?\(error.localizedDescription)")
}
})
.disposed(by: disposeBag)
這樣我們的錯(cuò)誤事件被包裝成了 Result.failure(Error) 元素,就不會(huì)終止整個(gè)序列。就算網(wǎng)絡(luò)請(qǐng)求失敗,整個(gè)訂閱依然存在。如果用戶再次點(diǎn)擊更新按鈕,也是能夠發(fā)起網(wǎng)絡(luò)請(qǐng)求進(jìn)行更新操作的。
除此之外,強(qiáng)烈建議閱讀 How to handle errors in RxSwift。
最后,對(duì)于初學(xué)者,建議將RxSwift代碼倉(cāng)庫(kù)下載到本地。
然后,打開(kāi) Rx.xcworkspace。
在左上角的 Schema 選中 RxSwift-macOS,然后 Build (快捷鍵:Command + B)項(xiàng)目。
在Build結(jié)束后,進(jìn)入 Project navigator (快捷鍵:Command + 1) 并找到 Rx - Rx.playground,然后打開(kāi)調(diào)試窗口(快捷鍵:Shift + Command + Y) 以查看示例代碼執(zhí)行效果。
在這個(gè)Playground中,官方對(duì) 所有的操作符 進(jìn)行了闡釋?zhuān)@可以幫助你迅速掌握RxSwift。
最后的最后,強(qiáng)烈建議初學(xué)者閱讀 RxSwift - Getting Started?。?!
參考文章:
ReactiveX Introduction
Github: ReactiveX / RxSwift
RxMarbles(常見(jiàn)寶石圖)
RxSwift - Getting Started
RxSwift 中文文檔 (本文大部分內(nèi)容來(lái)源于此文檔)
ReactiveX文檔中文翻譯
How to handle errors in RxSwift
如需轉(zhuǎn)載,請(qǐng)注明出處,謝謝 ~