LoginSignup
4

More than 3 years have passed since last update.

RxSwiftを理解するためにオペレータを整理してみた

Last updated at Posted at 2019-06-16

はじめに

RxSwiftには多くのオペレータが登場し、慣れるまでその理解が大変です。
そこで今回、RxSwift研究読本1と2を読んで登場したオペレータや、そのほか基本的なオペレータを整理してみました。
書籍の中では実装やテスト方法についても書かれているので、詳しくはそちらを読んでみると良いと思います。

オペレータ

正常系

filter

条件に合うもののみにフィルタリングする。

skip

先頭から指定した数だけイベントを無視してスキップする。

take

イベントを最初の指定した数だけに絞り、指定数に達した時点でcompletedになる。

map

新しいObservableインスタンスを返す。

flatMap

mapとの違い、型パラメータとしてObservableConvertibleTypeに準拠している必要があり、その返り値もそれに準拠している必要がある。

flatMapLatest

flatmapと違い、最新でないイベントを破棄する。

zip

複数のObservableを順番に組み合わせる。注意として一方のみの値が変っても更新されず、最新状態ではないことを認識しておく必要がある。

combineLatest

可変長でObservableを引数にとり、最後の引数にresultSelectorをとる。resultSelectorでは、各Observableの値が変わるたびに、その戻り値を次へと流れるObservableを返す。

withLatestFrom

combineLatestと違って、イベントの発行タイミングは最初のObservable の発行タイミング。

エラー系

retry

ストリームからのエラー通知時に、それをオブザーバーに通知せず、自動でストリームを再購読する。

catchError

Observableを返すことで、エラーを書き換えることができる。
エラーに応じて、自由にストリームを返したい場合や何も返したくない場合に使用する。

catchErrorJustReturn

catchErrorJustReturnは、catchErrorのラッパーとなっている。
任意の要素に書き換えたい場合に使用する。

RxSwift/Observables/Catch
extension ObservableType {
    public func catchError(_ handler: @escaping (Swift.Error) throws -> Observable<E>)
    -> Observable<E> {
    return Catch(source: asObservable(), handler: handler)
    }

    public func catchErrorJustReturn(_ element: E)
    -> Observable<E> {
       return Catch(source: asObservable(), handler: { _ in Observable.just(element) })
     }
}

ストリームの分岐

materialize

ObservableとしてたonNextイベントとonErrorイベントを一つの、Observable>として変換する。
2個のストリームを作成することで、正常系と異常系に関して、別々に購読できるようになる。

let observable = Observable<String>.create { observer in
     observer.onNext("A")
     observer.onError(TestError.test)

     return Disposables.create()
 }

_ = observable.materialize()
    .subscribe(onNext: { (event: Event<String>) in
        switch event {
        case .next(let value):
            print("value: \(value)")
        case .error(let error):
            print("error: \(error)")
        case .completed:
            break
        }
    }, onError: {
        print("onError, onError: \($0)")
    }, onCompleted: {
        print("onCompleted:")
    })

ただ正常系と異常系の分岐を、毎回書くのは面倒なので、後述するcompactMapを使用したextensionを作成すると良い。

compactMap

RxSift5.0で追加。

filterおよびmapメソッドをcompactMapメソッドでの書き換えでき、compactMapオペレータは、要素のnilをfilterしてmapしたストリームに変換してくれる。

extension ObservableType where Element: EventConvertible {

    public func elements() -> Observable<Element.Element> {
      return filter { $0.event.element != nil }
          .map { $0.event.element! }
    }

    public func errors() -> Observable<Swift.Error> {
       return filter { $0.event.error != nil }
          .map { $0.event.error! }
    }
}
extension ObservableType where Element: EventConvertible {

    public func elements() -> Observable<Element.Element> {
        return compactMap { $0.event.element }
    }

    public func errors() -> Observable<Swift.Error> {
        return compactMap { $0.event.error }
    }
}

Single等では、materializeオペレータがそのままは使えない

ObservableTypeに準拠していないSingleなどの、PrimitiveSequence型は"そのまま"ではmaterializeオペレータを使用できない。

  • PrimitiveSequence型

    • Single
      • 成功か失敗のみ送信できる
      • 完了は送信できない
      • いずれかのイベントを送信すると、disposeされる
    • Maybe
      • 成功、失敗、完了を送信することができる
      • いずれかのイベントを送信すると、disposeされる。
    • Completable
      • 完了か失敗のみ送信できる。
      • 成功は送信できない
      • いずれかのイベントを送信すると、disposeされる。

PrimitiveSequenceのStructとして、Single、Maybe、Completableは実装されている。
ジェネリクスでTraitsを切り替えることで、それらTraits(Single、Maybe、Completable)の振る舞いをするようになっている。
これらのTraitsはストリームを内部に保持したラッパーのため、materializeオペレータを使用できない。

そのためRxSwiftでは、PrimitiveSequenceのasObservable()で取り出せるように実装されている。

RxSwift/Traits/PrimitiveSequence
extension PrimitiveSequence: ObservableConvertibleType {
    /// Type of elements in sequence.
    public typealias E = Element

    /// Converts `self` to `Observable` sequence.
    ///
    /// - returns: Observable sequence that represents `self`.
    public func asObservable() -> Observable<E> {
        return source
    }
}

参考

RxSwift研究読本1 入門編
RxSwift研究読本2 エラーハンドリング編
https://qiita.com/k5n/items/e80ab6bff4bbb170122d
https://qiita.com/moaible/items/de94c574b25ea4f0ef17
https://qiita.com/k5n/items/17f845a75cce6b737d1e
https://qiita.com/crea/items/d46360e1eac709d6a632
https://qiita.com/bouzuya/items/d019c3eae5db19e395cd

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4