# 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 } } ```