LoginSignup
52
41

More than 5 years have passed since last update.

RxSwiftについてようやく理解できてきたのでまとめることにした(3)

Last updated at Posted at 2017-08-24

Observableの変換基礎 〜filter, map, flatMap〜

3つめの記事です。ここからが本番という感じです!
1つめの記事
2つめの記事

イベントを発生させるObservableですが、これらは変換・合成などができます。この章では基本的な3つの変換、

  • map
  • filter
  • flatMap

について書きます。

filter

let observable: Observable<Int> = Observable.from([1,2,3,4,5])
let filteredObservable: Observable<Int> = observable
    .filter { value in value > 3 }

元のObservableを、onNextで渡された値について、条件がtrueの要素のみからなるObservableに変換します。onCompletedやonErrorはそのまま流します。

上の例の場合は、

onNext(4) -> onNext(5) -> onCompleted()

map

let observable: Observable<Int> = Observable.from([1,2,3,4,5])
let mappedObservable: Observable<Int> = observable
    .map { value in value * 2 }

mapはonNextで流れる値を変換します。onErrorやonCompletedなどはそのまま流します。

上の例の場合は、

onNext(2) -> onNext(4) -> onNext(6) -> onNext(8) -> onNext(10) -> onCompleted()

中の実装によって、型が変わる場合もあります。

let observable: Observable<Int> = Observable.from([1,2,3,4,5])
let mappedObservable: Observable<String> = observable
    .map { value in "\(value)" }

この場合、元のobservableはObservable<Int>ですが、mapによりObservable<String>に変換されています。

flatMap

flatMapは一番良く使うOperatorで、RxSwiftによる処理の醍醐味のようなものです!
Promiseみたいな感じで、非同期の処理をどんどんつなげることができます。

値で説明するなら、以下のような感じです。

let observable: Observable<Int> = Observable.from([1,2,3,4,5])
let flatMappedObservable: Observable<Int> = observable
    .flatMap { value in Observable.from(0..<value) }

この例だと、onNextで

0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4

のあとにonCompletedが流れてきます。

区切るとわかりやすいと思いますが、それぞれの値に対してのObservableから流れる値が、連結されています。
(本当は並列処理などにより、順番が入れ替わる可能性があります)

flatMapでも、observableの型が変わる可能性があります。

中身のイベント

もちろん、中身のObservableの放つonNextは外側のobservableに流れます。
中身のObservableの放つonErrorも外側にも流れます。

よって、もし中身が一つでも失敗した場合は全体のsubscriptionが終了してしまいます。

これをしたくない場合は、以下のように中身のerrorを無視する必要があります。

// errorのときにデフォルト値を返す
.flatMap { value -> Observable<Int> in
    return Observable<Int>.error(NSError())
        .catchErrorJustReturn(1)
}

// errorのときに無視する
.flatMap { value -> Observable<Int> in
    return Observable<Int>.error(NSError())
        .catchError { error in
            return error
                .flatMap { _ inObservable<Int>.empty() }
        }
}

また、onCompletdは大元のonCompletedのみが使われて、
flatMap内のObservableのonCompletedは無視されます。

もし中身のonCompletedで全体のsubscriptionが終了してしまっては、
2つ目の要素が無視されてしまいます。

もし外側にも流したい場合は、少し工夫する必要があります。
(記事では書きませんが、takeUntilオペレータ などを使うと実現できます)

flatMapの利用例

例えば、ボタンがタップされたらデータを取ってきてラベルに反映する、という処理は以下のように書きます。

self.button.rx.tap
    .flatMap { [weak self] event in self?.fetchData() }
    .subscribe(onNext: { [weak self] data in
        self?.label.text = data.title
    })
    .disposed(by: self.disposeBag)

fetchData()はObservableを返します。

リクエストが複数段必要な場合も、どんどん繋げられるのがわかるかと思います!
(flatMapの後ろにまたflatMapをつなぐ)
こうすると、コールバック地獄のようにインデントが重ならないですね!

self.button.rx.tap
    .flatMap { [weak self] event in self?.fetchData() }
    .flatMap { [weak self] data in self?.fetchUser(data.userId) }
    .subscribe(onNext: { [weak self] user in
        self?.label.text = data2.title
    })
    .disposed(by: self.disposeBag)

ちなみに、以下のように書いても同じですが、インデント的に見にくいので、上のように書いたほうが良いです。
(ただし、エラーとかの扱いがちょっと変わるので、必要なときはあえて下のようにネストさせて書く場合もあります。)

self.button.rx.tap
    .flatMap { [weak self] event in 
        self?.fetchData()
            .flatMap { [weak self] data in self?.fetchUser(data.userId) }
    }
    .subscribe(onNext: { [weak self] user in
        self?.label.text = data2.title
    })
    .disposed(by: self.disposeBag)

オペレータ

こういうObservableを変換するもののことをオペレータと呼びます。
(ここまでで説明したのはfilter, map, flatMap

以下のように、オペレータによってObservableは変換されていきます。変換の途中でもObservableです。(Elementの型は変わることもあります)
※実際はそれぞれは新しいObservableです。

let barObservable = fooObservable
    .hogeオペレータ  // この時点でもobservable
    .fugaオペレータ  // この時点でもobservable
    .piyoオペレータ  // この時点でもobservable

上に上げたのは最も基礎的なオペレータで、他にも多くのオペレータがあります。
上でちらっと紹介した catchErrorJustReturn, catchError, takeUntilもオペレータの一つです。
他にも、take, merge, withLatestFrom, amb など様々なオペレータがあり、
必要に応じて使い分けるとより良くかけます!

が、とりあえずこの3つが使えれば基礎はOKかなと思います!
時間があればそれらについても説明したいと思います!

続き→https://qiita.com/_ha1f/items/2d0fc50505ce3a1fcdab

52
41
0

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
52
41