Try   HackMD

RxSwift 筆記教程

什麼是 RxSwift?

簡單來說,就是函數響應式(Functional Reactive Programming) + 觀察者模式(Observer Pattern)

舉例:

傳統的 Button 點擊事件:

let button = UIButton()
button.addTarget(self , action: #Selector(buttonTapped) , for: .touchUpInside)

func buttonTapped(){
    print("button Tapped")
}

以 Rx 的方式:

let button = UIButton()
button.rx.tap
         .subscribe(onNext:{
            print("button Tapped")
})
.disposed(by: disposeBag)

為什麼要用 RxSwift?

  • 複合 - Reactive Extension (Rx)
  • 複用 - 因為它容易整合
  • 清晰 - 因為有經過整合,所以宣告都是相似且不可變的
  • 易用 - 因為他有辦法將異步編程抽象化,使我們的編碼風格統一
  • 穩定

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():正常事件完成序列

Errorcompleted 結束後不會繼續觸發事件

而 Rx 中的 Observable 又分為兩種:

  • Finite Obervable Sequence

    • 指那些最後會以 completed 或 error 事件終極生命週期的可觀察對象,最典型的就是 API 的網路請求
    ​​​​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及交互事件,他們並不存在一個明確的生命週期終結點,例如:設備方向旋轉
    ​​​​UIDevice.rx.orientation
    ​​​​            .subscribe(onNext: { current in
    ​​​​        switch current{
    ​​​​        case .landscape:
    ​​​​            //re-arrange UI for landscape
    ​​​​        case .portrait:
    ​​​​            //re-arrange UI for portrait
    ​​​​        }
    ​​​​            })
    

Rx 中的操作符:

  • filter : 過濾
  • map : 轉換元素
  • bind : 為綁定,能將 observe 到的值綁定到 object 或 element上
  • throttle :
  • zip : 組合多個Observable的元素
  • just : 產生一個特定的元素
  • from : 從一個序列垃曲元素
  • repeatElement : 重複的產生某一個元素
  • create : 存在自定義邏輯
  • deferred : 每次訂閱時產生
  • interval : 每隔一段時間,發出一個元素
  • empty : 一個空序列,只有一件完成事件
  • never : 一個任何事件都沒有產生的序列
  • merge : 任意一個Observable產生了元素,就發出這個元素
  • concat : 讓這些Observables一個接一個的發出元素,當一個Observable元素發送完畢後,下一個Observable才能開始發出元素
  • delay : 將產生的每一個元素,拖延一段時間後再發出
  • disposed : 為最後完成事件後做一次資源的回收,因為每一個綁定都是有生命週期的,並且這個綁定是可以被清除的。此方法就是把綁定的生命週期交給 DisposeBag 來處理,當 DisposeBag 被釋放的時候,那麼裡面尚未被清除的綁定也會一併被清除,這也相當於用 ARC 來管理綁定的生命週期

Subjects

  1. PublishSubject :

    • 將對觀察者發送訂閱後產生的元素,而在訂閱前發送的元素將不會發送給觀察者

    ex:

    ​​​​let subject = PublishSubject<String>()
    ​​​​subject.onNext("A")
    ​​​​subject.subscribe(onNext: {
    ​​​​    print($0)
    ​​​​})
    ​​​​.disposed(by: disposeBag)
    ​​​​subject.onNext("B")
    ​​​​subject.onNext("C")
    

    output:

    ​​​​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
  1. BehaviorSubject :

    • 當觀察者對 BehaviorSubject 進行訂閱的時候,他會將 Observable 中最新的元素發送出來。如果不存在最新的元素,就會發送默認元素,然後將隨後產生出來的元素發送出來

    ex:

    ​​​​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:

    ​​​​訂閱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 時,發送出去
  1. ReplaySubject :

    • 無論觀察者是何時進行訂閱的,ReplaySubject 都將對觀察者發送全部的元素

    ex:

    ​​​​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:

    ​​​​2
    ​​​​3
    ​​​​4
    ​​​​5
    ​​​​6
    
  • onNext 函數調用的是 ReplayBufferBaseon 函數
  • 在將 observer 加入到 self._observers 之前,先調用了self.replayBuffer(anyObserver)會執行 ReplayManyBase.replayBuffer 函數。
override func replayBuffer<O: ObserverType>(_ observer: O) where O.E == Element {
    for item in self._queue {
        observer.on(.next(item))
    }
}
  • 將保存在隊列 queue 中的所有元素,發送給訂閱者
  1. AsyncSubject :

    • 將在 Observable 產生完成時間之後,發出最後一個元素(有且僅有最後一個元素),如果 Observable 沒有發出任何元素,只有一個完成事件,則 AsyncSubject 也只有一個完成事件。如果 Observable 產生了一個 error 事件而中止,那麼 AsyncSubject 就不會發出任何元素,而是將 error 事件發送出來

    ex:

    ​​​​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:

    ​​​​next(4)
    ​​​​completed
    

Relays

  1. PublishRelay:
    • 定義『只接受.next事件,不接受.error或.completed』,換句話說,它不會中斷,從source code來看,PublishRelay其實就是PublishSubject再包一層,因為只能接收.next,所以語法變成.accept了。
  2. BehaviorRelay:
    • 定義的部分跟PublishRelay一樣,BehaviorRelay就是BehaviorSubject的封裝,跟BehaviorSubject一樣有.value()可以使用,但因為Relay不會.error或disposed的問題,自然也不用處理意外狀況。

RxSwift 核心

  • Observable - 產生事件
  • Observer - 響應事件
  • Operator - 創建變化組合事件
  • Dispossble - 管理綁定(訂閱)的生命週期
  • Schedulers - 線程隊列排序both#1-Singl/)

RxSwift 中常用的組合技能

  1. merge: 將兩條observable合併在一起
  2. withLatestFrom: 將本身與()內最後的狀態做組合運算
  3. combinLatest: 將兩條observable最後的狀態組合運算
  4. distinctUntilChanged: 自動忽略重複的signal
  5. filter: 一條件將observable過濾
  6. throttle: 在設定時間內連續出現的signal將被忽略
  7. map: 將observable重新依照運算產出

RxSwift 中 Observable 的特徵序列

  1. Single
  2. Completable
  3. Maybe
  4. Driver
  5. Signal
  6. ControlEvent

Single

Observable的另外一個版本,不像Observable可以發出多個元素,他要嘛只能發出一個元素,要嘛產生一個 error 事件

  • 發出一個元素或是一個 error 事件
  • 不會共享附加作用
    一個比較常見的粒子就是執行 http 請求,然後返回一個答案或是錯誤

創建方法:

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) 

同樣可以對Observable調用.asSingle來將他轉為Single

Completable

同樣是Observable的另外一個版本,不像Observable可以發出多個元素,他要嘛只能產生一個 completed 事件,要嘛產生一個 error 事件

  • 不會發出元素
  • 發出一個 completed 事件或是一個 error 事件
  • 不會共享附加作用
    Completable適用於你關心的任務是否完成,而不需要在意任務返回值的奇框,這點跟 Observable<Void> 有點類似

如何創建:

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) 

Maybe

他介於SingleCompletable之間。他要嘛只能發出一個元素,要嘛產生一個 completed 事件,要嘛產生一個 error 事件

  • 發出一個元素或者一個 completed 事件或是一個 error 事件
  • 不會共享附加作用
    如果遇到那種可能需要發出一個元素,又可能不需要發出的時候就可以用 Maybe

如何創建:

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)

同樣可以對 Observable 調用.asMaybe() 將它轉換為 Maybe

Driver

是一個精心準備的特徵序列。他主要是為了簡化 UI 層的代碼,過如果你遇到序列具有以下特徵:

  • 不會產生 error 事件
  • 一定在 MainScheduler 監聽(主線程監聽)
  • 共享附加作用
    你也可以使用它,這些都是驅動 UI 的序列所具有的特徵

為什麼要使用 Driver:

有時與 UI 互動的網路請求若有一個錯誤,這個錯誤將取消所有綁定,導致用戶在輸入新的關鍵字時沒有辦法發起新的網路請求,同時若成功的 item 從後台返回序列,那麼 UI 的更新也會在後台進行,就會造成異常崩潰
再越大型的系統內想要確保每一部不被遺漏是一件困難的事,因此需要運用合理的特徵序列來處理,來避免錯誤

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

SignalDriver相似,唯一的區別是,Driver會對新觀察者回放(重新發送)上一個元素,而 Signal不會對新觀察者回放上一個元素

  • 不會產生 error 事件
  • 一定在 MainScheduler 監聽(主線程監聽)
  • 共享附加作用

ControlEvent/ControlProperty

專門用於描述 UI 控件所產生的事件,他具有以下特徵:

  • 不會產生 error 事件
  • 一定在 MainScheduler 監聽(主線程監聽)
  • 一定在 MainScheduler 訂閱(主線程訂閱)
  • 共享附加作用

RxSwift 中 Observer 的特徵觀察者

  1. AnyObserver
  2. 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寫法:

// 后台取得数据,主线程处理结果
DispatchQueue.global(qos: .userInitiated).async {
    let data = try? Data(contentsOf: url)
    DispatchQueue.main.async {
        self.data = data
    }
}

如果用 RxSwift 實現:

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 可以讓序列發生在錯誤後重試:

// 请求 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實現:

// 请求 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來控制重試的機制:

.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
    ...
}

閉包裡面的參數是Observable<Error>也就是所產生錯誤的序列,然後返回值是一個Observable,當這個返回值的Observable發出一個元素時,就進行重試操作。當他發出一個 error 或者 completed 事件時就不會重試,並且將這個事件傳遞到後面的觀察者
如果需要加上一個最大重試次數的設定:

// 请求 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


catchError可以在錯誤產生時,用一個備用元素或者一組備用元素將錯誤替換掉,這樣可以使得Observable正常結束或者根本不需要結束:

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:

next(😬)
next(😨)
next(😡)
next(🔴)
Error: test
next(😊) 

result

如果只是想要給用戶錯誤提示,以下提供一個最為直接的方案:

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,然後只發出她的元素

當你傳入多個Observablesamb時,他將取其中一個Observableamb將忽略掉其他的Observables

buffer

緩存元素,然後將緩存的元素集合,週期性的發出來

buffer將緩存Observable中發出的新元素,當元素達到某個數量,或者經過了特定的時間,他就會將這個元素集合發送出來

catchErrorJustReturn

catchErrorJustReturn會將 error 事件替換成其他一個元素,然後結束該序列:

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:

next(😬)
next(😨)
next(😡)
next(🔴)
next(😊)
completed

RxSwift 常用結構

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,而且他還提供動畫支持

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)
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
    }
}