# RxSwift 筆記教程
## 什麼是 RxSwift?
#### 簡單來說,就是函數響應式(Functional Reactive Programming) + 觀察者模式(Observer Pattern)
<style>
h4 {
color: darkblue;
}
</style>
<style>
h3 {
color: darkred;
}
</style>
### 舉例:
傳統的 Button 點擊事件:
```swift
let button = UIButton()
button.addTarget(self , action: #Selector(buttonTapped) , for: .touchUpInside)
func buttonTapped(){
print("button Tapped")
}
```
以 Rx 的方式:
```swift
let button = UIButton()
button.rx.tap
.subscribe(onNext:{
print("button Tapped")
})
.disposed(by: disposeBag)
```
---
## 為什麼要用 RxSwift?
- [x] 複合 --- Reactive Extension (Rx)
- [x] 複用 --- 因為它容易整合
- [x] 清晰 --- 因為有經過整合,所以宣告都是相似且不可變的
- [x] 易用 --- 因為他有辦法將異步編程抽象化,使我們的編碼風格統一
- [x] 穩定
#### RxSwift 的 Observable 可以非同步的接受元素或者對象
* 一個 Observable (ObservableType),相當於一個 Sequence
* `ObservableType.subscribe(_:)` 相當於 `Sequance.makeIterator()`
* Observer(callback) 需要傳遞給 ObservableType.subscribe 方法來接受序列裡的元素,是用呼叫 `onNext(_:)` 來返回
#### 在 Rx 中,`Observable<Element>` 代表的是一個可觀察序列,從字面上的意思可以看出這是在觀察者模式中的觀察者,他會向觀察對象發送事件序列
1. onNext(Element):代表新事件,他會將可觀察對象的最新值傳給觀察者
2. onError(ErrorTypre):帶有異常的完成序列
3. onCompleted():正常事件完成序列
-- <font color="#f00">Error</font> 跟 <font color="#f00">completed</font> 結束後不會繼續觸發事件
#### 而 Rx 中的 Observable 又分為兩種:
- ## Finite Obervable Sequence
- 指那些最後會以 completed 或 error 事件終極生命週期的可觀察對象,最典型的就是 API 的網路請求
```swift
API.download(file: "https://~~~~~~~~")
.subscribe(onNext: { data in
//append data to file
},
onError: { error in
//display error tp user
},
onCompleted: {
//use downloaded files
})
- ## Infinite Observable Sequence
- 指UI及交互事件,他們並不存在一個明確的生命週期終結點,例如:設備方向旋轉
```swift
UIDevice.rx.orientation
.subscribe(onNext: { current in
switch current{
case .landscape:
//re-arrange UI for landscape
case .portrait:
//re-arrange UI for portrait
}
})
```
#### Rx 中的操作符:
* <font color="#FF161616">filter</font> : 過濾
* <font color="#FF161616">map</font> : 轉換元素
* <font color="#FF161616">bind</font> : 為綁定,能將 observe 到的值綁定到 object 或 element上
* <font color="#FF161616">throttle</font> :
* <font color="#FF161616">zip</font> : 組合多個`Observable`的元素
* <font color="#FF161616">just</font> : 產生一個特定的元素
* <font color="#FF161616">from</font> : 從一個序列垃曲元素
* <font color="#FF161616">repeatElement</font> : 重複的產生某一個元素
* <font color="#FF161616">create</font> : 存在自定義邏輯
* <font color="#FF161616">deferred</font> : 每次訂閱時產生
* <font color="#FF161616">interval</font> : 每隔一段時間,發出一個元素
* <font color="#FF161616">empty</font> : 一個空序列,只有一件完成事件
* <font color="#FF161616">never</font> : 一個任何事件都沒有產生的序列
* <font color="#FF161616">merge</font> : 任意一個`Observable`產生了元素,就發出這個元素
* <font color="#FF161616">concat</font> : 讓這些`Observables`一個接一個的發出元素,當一個`Observable`元素發送完畢後,下一個`Observable`才能開始發出元素
* <font color="#FF161616">delay</font> : 將產生的每一個元素,拖延一段時間後再發出
* <font color="#FF161616">disposed</font> : 為最後完成事件後做一次資源的回收,因為每一個綁定都是有生命週期的,並且這個綁定是可以被清除的。此方法就是把綁定的生命週期交給 DisposeBag 來處理,當 DisposeBag 被釋放的時候,那麼裡面尚未被清除的綁定也會一併被清除,這也相當於用 ARC 來管理綁定的生命週期
---
## Subjects
1. __PublishSubject__ :
- 將對觀察者發送訂閱後產生的元素,而在訂閱前發送的元素將不會發送給觀察者
![](https://i.imgur.com/bYS4JBL.png)
![](https://i.imgur.com/LfYlaH4.png)
ex:
```swift
let subject = PublishSubject<String>()
subject.onNext("A")
subject.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
subject.onNext("B")
subject.onNext("C")
```
output:
```swift
B
C
```
- __PublishSubject__ 既是 *可監聽序列*,也是 *觀察者*
- 在第一次執行 `onNext`時,會執行`PublishSubject.on`函數,並繼續調用 dispatch 函數和 `self._synchronized_on(event)`函數
- `self._synchronized_on(event)`函數最終會返回`self._observers`
- 而`self._observers`的類型是`Bag<(Event<Element>) -> Void>`
- 因此可以得知其作用就是對 bag 中保存的代碼進行回調
- 因為上一步傳入的 bag 僅是一個初始化的 bag,中間並沒有任何代碼,所以不會執行任何調用,到這裡也表示onNext已經結束了,因此沒有任何output
- 調用 `subscribe`
- 在 __PublishSubject__ 中實現 subscribe
- 最終會調用 `let key = self._observers.insert(observer.on)`
- 因為是第首次執行,所以 `_dictionary` 值為nil,且 `_pairs.count` 小於 `arrayDictionaryMaxSize`,所以,觀察者的 on函數 `observer.on` 被加入到 `_pairs` 數組中。
- 第二次執行 `onNext`函數,步驟跟第一次一樣,只是最後執行 dispatch 函數時,因為`bag._pairs`有保存一個觀察者的 on 函數代碼,所以會執行回調
- 最終會回調到`subscribe.onNext`閉包,產生output
2. __BehaviorSubject__ :
- 當觀察者對 __BehaviorSubject__ 進行訂閱的時候,他會將 Observable 中最新的元素發送出來。如果不存在最新的元素,就會發送默認元素,然後將隨後產生出來的元素發送出來
![](https://i.imgur.com/S4FQNO3.png)
![](https://i.imgur.com/N4USmTW.png)
ex:
```swift
let subject = BehaviorSubject<Int>(value: 100)
subject.subscribe(onNext: {
print("訂閱1: \($0)")
})
.disposed(by: disposeBag)
subject.onNext(3)
subject.onNext(5)
subject.subscribe(onNext: {
print("訂閱2: \($0)")
})
.disposed(by: disposeBag)
```
output:
```swift
訂閱1: 100
訂閱1: 3
訂閱1: 5
訂閱2: 5
```
- __BehaviorSubject__ 的初始化方法需要傳入一個初始值作為默認元素
- __BehaviorSubject__ 在 `_synchronized_subscribe` 函數中比 __PublishSubject__ 多一行代碼,執行了一次 `observer.on` 函數,並將 `self._element` 作為參數傳遞。 `self._element` 中保存的是最新發送的元素,如果沒有最新元素,則為 `init` 初始化時的默認元素
- __BehaviorSubject__ 在調用 onNext 時,會將最新的元素保存在 `self._element` 中,在執行執行 `subscribe` 時,發送出去
3. __ReplaySubject__ :
- 無論觀察者是何時進行訂閱的,__ReplaySubject__ 都將對觀察者發送全部的元素
ex:
```swift
let subject = ReplaySubject<Int>.create(buffersize: 2)
subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
subject.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
subject.onNext(4)
subject.onNext(5)
subject.onNext(6)
```
output:
```swift
2
3
4
5
6
```
- `onNext` 函數調用的是 `ReplayBufferBase` 的 `on` 函數
- 在將 observer 加入到 `self._observers` 之前,先調用了`self.replayBuffer(anyObserver)`會執行 `ReplayManyBase.replayBuffer` 函數。
```swift
override func replayBuffer<O: ObserverType>(_ observer: O) where O.E == Element {
for item in self._queue {
observer.on(.next(item))
}
}
```
- 將保存在隊列 queue 中的所有元素,發送給訂閱者
4. __AsyncSubject__ :
- 將在 Observable 產生完成時間之後,發出最後一個元素(有且僅有最後一個元素),如果 Observable 沒有發出任何元素,只有一個完成事件,則 __AsyncSubject__ 也只有一個完成事件。如果 Observable 產生了一個 error 事件而中止,那麼 __AsyncSubject__ 就不會發出任何元素,而是將 error 事件發送出來
ex:
```swift
let subject = AsyncSubject<Int>()
subject.onNext(1)
subject.onNext(2)
subject.subscribe({
print($0)
})
.disposed(by: disposeBag)
subject.onNext(3)
subject.onNext(4)
subject.onCompleted()
```
output:
```swift
next(4)
completed
```
---
## Relays
4. __PublishRelay__:
- 定義『只接受.next事件,不接受.error或.completed』,換句話說,它不會中斷,從source code來看,PublishRelay其實就是PublishSubject再包一層,因為只能接收.next,所以語法變成.accept了。
5. __BehaviorRelay__:
- 定義的部分跟PublishRelay一樣,BehaviorRelay就是BehaviorSubject的封裝,跟BehaviorSubject一樣有.value()可以使用,但因為Relay不會.error或disposed的問題,自然也不用處理意外狀況。
## RxSwift 核心
![](https://i.imgur.com/gBJ1CWg.png)
* Observable --- 產生事件
* Observer --- 響應事件
* Operator --- 創建變化組合事件
* Dispossble --- 管理綁定(訂閱)的生命週期
* Schedulers --- 線程隊列排序both#1-Singl/)
---
## RxSwift 中常用的組合技能
1. merge: 將兩條observable合併在一起
- ![](https://i.imgur.com/wk3YTyd.png)
2. withLatestFrom: 將本身與()內最後的狀態做組合運算
- ![](https://i.imgur.com/cbfolMr.png)
3. combinLatest: 將兩條observable最後的狀態組合運算
- ![](https://i.imgur.com/zsD6oI3.png)
4. distinctUntilChanged: 自動忽略重複的signal
- ![](https://i.imgur.com/ZjOa4Z3.png)
5. filter: 一條件將observable過濾
- ![](https://i.imgur.com/z3XtzrR.png)
6. throttle: 在設定時間內連續出現的signal將被忽略
- ![](https://i.imgur.com/5y4UMmA.png)
7. map: 將observable重新依照運算產出
- ![](https://i.imgur.com/LMLLxkT.png)
## RxSwift 中 Observable 的特徵序列
1. [Single](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Single)
2. [Completable](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Completable)
3. [Maybe](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Maybe)
4. [Driver](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Driver)
5. [Signal](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Signal)
6. [ControlEvent](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#ControlEvent)
### Single
是`Observable`的另外一個版本,不像`Observable`可以發出多個元素,他要嘛只能發出一個元素,要嘛產生一個 error 事件
* 發出一個元素或是一個 error 事件
* 不會共享附加作用
一個比較常見的粒子就是執行 http 請求,然後返回一個答案或是錯誤
#### 創建方法:
```swift
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() }
}
}
```
#### 如何調用:
```swift
getRepo("ReactiveX/RxSwift")
.subscribe(onSuccess: { json in
print("JSON: ", json)
}, onError: { error in
print("Error: ", error)
})
disposed(by: disposeBag)
```
同樣可以對`Observable`調用`.asSingle`來將他轉為`Single`
### Completable
同樣是`Observable`的另外一個版本,不像`Observable`可以發出多個元素,他要嘛只能產生一個 completed 事件,要嘛產生一個 error 事件
* 不會發出元素
* 發出一個 completed 事件或是一個 error 事件
* 不會共享附加作用
`Completable`適用於你關心的任務是否完成,而不需要在意任務返回值的奇框,這點跟 `Observable<Void>` 有點類似
#### 如何創建:
```swift
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 {}
}
```
#### 如何調用:
```swift
cacheLocally()
.subscribe(onCompleted: {
print("Completed with no error")
}, onError: { error in
print("Completed with an error: \(error.localizedDescription)")
})
.disposed(by: disposeBag)
```
### Maybe
他介於`Single`和`Completable`之間。他要嘛只能發出一個元素,要嘛產生一個 completed 事件,要嘛產生一個 error 事件
* 發出一個元素或者一個 completed 事件或是一個 error 事件
* 不會共享附加作用
如果遇到那種可能需要發出一個元素,又可能不需要發出的時候就可以用 Maybe
#### 如何創建:
```swift
func generateString() -> Maybe<String> {
return Maybe<String>.create { maybe in
maybe(.success("RxSwift"))
// OR
maybe(.completed)
// OR
maybe(.error(error))
return Disposables.create {}
}
}
```
#### 如何調用:
```swift
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)
```
同樣可以對 Observable 調用`.asMaybe()` 將它轉換為 Maybe
### Driver
是一個精心準備的特徵序列。他主要是為了簡化 UI 層的代碼,過如果你遇到序列具有以下特徵:
* 不會產生 error 事件
* 一定在 MainScheduler 監聽(主線程監聽)
* 共享附加作用
你也可以使用它,這些都是驅動 UI 的序列所具有的特徵
#### 為什麼要使用 Driver:
有時與 UI 互動的網路請求若有一個錯誤,這個錯誤將取消所有綁定,導致用戶在輸入新的關鍵字時沒有辦法發起新的網路請求,同時若成功的 item 從後台返回序列,那麼 UI 的更新也會在後台進行,就會造成異常崩潰
再越大型的系統內想要確保每一部不被遺漏是一件困難的事,因此需要運用合理的特徵序列來處理,來避免錯誤
```swift
let results = query.rx.text.asDriver() // 将普通序列转换为 Driver
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // 仅仅提供发生错误时的备选返回值
}
results
.map { "\($0.count)" }
.drive(resultCount.rx.text) // 这里改用 `drive` 而不是 `bindTo`
.disposed(by: disposeBag) // 这样可以确保必备条件都已经满足了
results
.drive(resultsTableView.rx.items(cellIdentifier: "Cell")) {
(_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
```
任何可監聽序列都可以被轉為 Driver ,只要滿足上面三個條件
而在`Driver`裡使用`drive()`而不是`bind(to:_)`,因為`drive()`方法只能被`Driver`調用,這表示如果發現代碼存在`drive()`那麼這個序列不會產生錯誤事件並且一定在主線程監聽,這樣就可以安全地進行 UI 綁定
### Signal
`Signal`跟`Driver`相似,唯一的區別是,`Driver`會對新觀察者回放(重新發送)上一個元素,而 `Signal`不會對新觀察者回放上一個元素
* 不會產生 error 事件
* 一定在 MainScheduler 監聽(主線程監聽)
* 共享附加作用
### ControlEvent/ControlProperty
專門用於描述 UI 控件所產生的事件,他具有以下特徵:
* 不會產生 error 事件
* 一定在 MainScheduler 監聽(主線程監聽)
* 一定在 MainScheduler 訂閱(主線程訂閱)
* 共享附加作用
---
## RxSwift 中 Observer 的特徵觀察者
1. [AnyObserver](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#AnyObserver)
2. [Binder](https://hackmd.io/R6HpVANzRpa3H8egBuiMiQ?both#Binder)
### AnyObserver
可以用來描述任何一種觀察者
### Binder
主要以下兩個特徵:
1. 不會處理錯誤事件
2. 確保綁定都是給定 Scheduler 上執行(默認 MainScheduler)
一旦發生錯誤事件,在調適環境下執行`fatalError`,在發布環境下將打印錯誤訊息`Binder`可以只處理 next 事件,並且保證響應 next 事件的代碼一定會在`Scheduler`上執行
---
## Rx 中取消訂閱的方式
1. __DisposeBag__ :
- 因為是`Swift`,所以我們更習慣於使用 ARC 來管理內存,因此我們依舊可以用 ARC 來管理我們的訂閱生命週期,就是用`DisposeBag`(清除包)來實現訂閱管理機制
- 當`DisposeBag` 被釋放的時候,`DisposeBag`內部的所有可被清除的資源都將被清除
2. __takeUntil__ :
- 能夠建立一個條件,當條件滿足時竟會自動把訂閱取消掉
---
## Schedulers
#### `Schedulers`(調節器)是 Rx 實現多線程核心模式,他主要用於控制任務在哪個線程或列隊運行
ex: 原本GCD寫法:
```swift
// 后台取得数据,主线程处理结果
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
self.data = data
}
}
```
如果用 RxSwift 實現:
```swift
let rxData: Observable<Data> = ...
rxData
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] data in
self?.data = data
})
.disposed(by: disposeBag)
```
- 使用`subscribeOn`:
- 用`subscribeOn`來決定數據序列的構建函數在哪個`Scheduler`上運行,由上面例子來說,由於獲取 data 需要花很長的時間,所以用`subscribeOn`切換到後台`Scheduler`來獲取 data,就可以避免主線程被阻塞
- 使用`observeOn`:
- 用`obseveOn`來決定在哪個`Scheduler`監聽這個數據序列,由上面例子來說,通過使用`observeOn`方法切換到主線程來監聽應且處理結果
#### 一個比較典型的例子就是,在後台發起網路請求,然後解析數據,最後在主線程刷新頁面。可以先用`subscribeOn`切到後台去發送請求並解析數據,最後用`observeOn`切換到主線程更新頁面
* ## MainScheduler
-`MainScheduler`表示主線程。如果你需要執行一些和 UI 相關的任務,就需要切換到該`Scheduler`運行
* ## SerailDispatchQueueScheduler
- 他抽象化了串行`DispatchQueue`。如果你需要執行一些串行任務,可以切換到這個`Scheduler`運行
* ## ConcurrentDispatchQueueScheduler
- 他抽象化了並行`DispatchQueue`。如果你需要執行一些並行任務,可以切換到這個`Scheduler`運行
* ## OperationQueueScheduler
- 他抽象化了`NSOperationQueue`。他具備`NSOperationQueue`的一些特點,例如,你可以通過設置`maxConcurrentOperationCount`來控制同時執行併發任務的最大數量
---
## Error Handling
#### 一旦序列裡面產出了一個 error 事件,整個序列將被終止, RxSwift 主要有兩種錯誤處理機制:
1. retry --- 重試
2. catch --- 恢復
3. result
4. amb
5. buffer
6. catchErrorJustReturn
### retry
retry 可以讓序列發生在錯誤後重試:
```swift
// 请求 JSON 失败时,立即重试,
// 重试 3 次后仍然失败,就将错误抛出
let rxJson: Observable<JSON> = ...
rxJson
.retry(3)
.subscribe(onNext: { json in
print("取得 JSON 成功: \(json)")
}, onError: { error in
print("取得 JSON 失败: \(error)")
})
.disposed(by: disposeBag)
```
以上的代碼`retry(3)`就是當發生錯誤時,就進行重試操作,並且最多重試3次
如果我們需要再發生錯誤時,經過一段延遲後重試,就可以用`retryWhen`實現:
```swift
// 请求 JSON 失败时,等待 5 秒后重试,
let retryDelay: Double = 5 // 重试延时 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return Observable.timer(retryDelay, scheduler: MainScheduler.instance)
}
.subscribe(...)
.disposed(by: disposeBag)
```
這裡我們需要用到`retryWhen`,他主要敘述應該在何時重試,並且通過閉包裡面返回的`Observable`來控制重試的機制:
```swift
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
...
}
```
閉包裡面的參數是`Observable<Error>`也就是所產生錯誤的序列,然後返回值是一個`Observable`,當這個返回值的`Observable`發出一個元素時,就進行重試操作。當他發出一個 error 或者 completed 事件時就不會重試,並且將這個事件傳遞到後面的觀察者
如果需要加上一個最大重試次數的設定:
```swift
// 请求 JSON 失败时,等待 5 秒后重试,
// 重试 4 次后仍然失败,就将错误抛出
let maxRetryCount = 4 // 最多重试 4 次
let retryDelay: Double = 5 // 重试延时 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)
```
### catchError
![](https://i.imgur.com/TayJCVo.png)
`catchError`可以在錯誤產生時,用一個備用元素或者一組備用元素將錯誤替換掉,這樣可以使得`Observable`正常結束或者根本不需要結束:
```swift
let disposeBag = DisposeBag()
let sequenceThatFails = PublishSubject<String>()
let recoverySequence = PublishSubject<String>()
sequenceThatFails
.catchError {
print("Error:", $0)
return recoverySequence
}
.subscribe { print($0) }
.disposed(by: disposeBag)
sequenceThatFails.onNext("😬")
sequenceThatFails.onNext("😨")
sequenceThatFails.onNext("😡")
sequenceThatFails.onNext("🔴")
sequenceThatFails.onError(TestError.test)
recoverySequence.onNext("😊")
```
output:
```swift
next(😬)
next(😨)
next(😡)
next(🔴)
Error: test
next(😊)
```
### result
如果只是想要給用戶錯誤提示,以下提供一個最為直接的方案:
```swift
public enum Result<Success, Failure> where Failure : Error {
case success(Success)
case failure(Failure)
}
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Result<Void, Error>> in
return update(userInfo)
.map(Result.success) // 转换成 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)
```
如此一來將錯誤事件包裝成了`Result.failure(Error)`元素,就不會終止整個程序,即便網路請求失敗了,整個訂閱依然存在,如果用戶再次點擊按鈕,也是能夠發起網路請求進行操作
### amb
在多個`Observables`中,取第一個發出元素或產生事件的`Observable`,然後只發出她的元素
![](https://i.imgur.com/TiJjgaO.png)
當你傳入多個`Observables`到`amb`時,他將取其中一個`Observable`,`amb`將忽略掉其他的`Observables`
### buffer
緩存元素,然後將緩存的元素集合,週期性的發出來
![](https://i.imgur.com/1GvrvIh.png)
`buffer`將緩存`Observable`中發出的新元素,當元素達到某個數量,或者經過了特定的時間,他就會將這個元素集合發送出來
### catchErrorJustReturn
`catchErrorJustReturn`會將 error 事件替換成其他一個元素,然後結束該序列:
```swift
let disposeBag = DisposeBag()
let sequenceThatFails = PublishSubject<String>()
sequenceThatFails
.catchErrorJustReturn("😊")
.subscribe { print($0) }
.disposed(by: disposeBag)
sequenceThatFails.onNext("😬")
sequenceThatFails.onNext("😨")
sequenceThatFails.onNext("😡")
sequenceThatFails.onNext("🔴")
sequenceThatFails.onError(TestError.test)
```
output:
```swift
next(😬)
next(😨)
next(😡)
next(🔴)
next(😊)
completed
```
---
## RxSwift 常用結構
![](https://i.imgur.com/fCDn36e.png)
#### `RxSwift` 是一個響應式編程的基礎框架,他並不會強制要求你使用某種架構,他和多個應用程序完美適配:
* MVVM --- 設計模式
* RxFeedback --- RxSwift 提供的反饋循環結構
* ReactorKit --- 結合了 Flux 和響應式編程的架構
---
## RxSwift 生態系統
#### `RxCocoa` 給 UI 框架提供了 Rx 的支持,讓我們能夠使用按鈕點擊序列,輸入框的當前文字等等。不過 `RxCocoa` 也只是 `RxSwift` 生態系統中的一員。`RxSwift` 生態系統還給其他框架提供了 Rx 支持:
* RxdataSources --- `UITableView` & `UICollectionView`數據庫
* RxGesture --- 頁面手勢
* RxMKMapView --- 地圖
* RxCoreMotion --- 陀螺儀
* RxAlamofire --- 網路請求
* RxCoreData --- `CoreData`數據庫
* RxRealm --- `Realm`數據庫
* RxMediaPicker --- 圖片選擇器
* Action --- 行為
* RxWebKit --- `WebView`
* RxEventHub --- 全局通知
* RxSwiftExt --- 添加一些有用的操作符
### RxDataSources
書寫 tableView 或 collectionView 的數據源是一件非常瑣碎的事情,有一大堆的代理方法需要被執行。`RxdataSources`可以幫助簡化這個過程,你只需要幾行代碼就可以佈局 view,而且他還提供動畫支持
```swift
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>()
Observable.just([SectionModel(model: "title", items: [1, 2, 3])])
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
```
```swift
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Double>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
return cell
},
titleForHeaderInSection: { dataSource, sectionIndex in
return dataSource[sectionIndex].model
}
)
override func viewDidLoad() {
super.viewDidLoad()
let dataSource = self.dataSource
let items = Observable.just([SectionModel(model: "First section", items: [
1.0,
2.0,
3.0
]),
SectionModel(model: "Second section", items: [
1.0,
2.0,
3.0
]),
SectionModel(model: "Third section", items: [
1.0,
2.0,
3.0
])
])
items
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
tableView.rx
.itemSelected
.map { indexPath in
return (indexPath, dataSource[indexPath])
}
.subscribe(onNext: { pair in
DefaultWireframe.presentAlert("Tapped `\(pair.1)` @ \(pair.0)")
})
.disposed(by: disposeBag)
tableView.rx
.setDelegate(self)
.disposed(by: disposeBag)
}
// to prevent swipe to delete behavior
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
```