この記事はRxをちょっと手を出したけどどう勉強すればいいかわからない人が、なんとなくコードを書けるようになるための記事です
やってるところでは当たり前になってるRxですが、まだまだ敷居が高くて手が出せないという方も多いのでは無いでしょうか?
そんな方たちに向けて、自分はこんなふうに勉強してどうにかコード書けるようになったよ!というのをまとめたいと思います
必ずしも上から順番にしっかり理解しなければいけないわけではないので、よくわからないものがあっても、とりあえず次に進んでください
記事中に簡単に説明を書きますが、だいぶ自分が直感的に理解したもので間違ってる(正しくない)可能性があります
参考記事を載せるので、正確な説明はそちらを見てください
Observable(とその派生)
Rxを使っていると色々なプロパティと出会います。それぞれの性質や用途を把握しましょう。
Observable
すべての基本。こいつを購読(subscribe)するとonNext、onError、onCompletedの3つのイベントが流れてくる。これを使ってもいいが、用途別にいろいろな派生が存在する。他の派生が使えないときに使うイメージ。
参考:
https://qiita.com/k5n/items/17f845a75cce6b737d1e#observable
https://qiita.com/_ha1f/items/e16ddc6017c4ad807c3c#observable%E5%85%A5%E9%96%80
(Publish|Behaviour)Subject
外から値を詰め込んでイベントを流すことができるObservable。元のObservableは生成時に指定した値でしかイベントが発生しない。
PublishとBehaviourの違いはキャッシュを持つかどうか。BehaviourSubjectは最後に流された値をキャッシュとして持っていて、購読した際にその値がすぐに流れてくる。PublishSubjectは購読後に発生したイベントのみ流れてくる。
任意のタイミングで変更される値をイベントとして扱いたいときに使うイメージ。ViewModelのプロパティはこれかRelayかDriverが多い気がする。
Subjectには他にも種類があるが自分は使わないので良く知らない。。。
参考:
https://qiita.com/k5n/items/17f845a75cce6b737d1e#publishsubject
https://qiita.com/k5n/items/17f845a75cce6b737d1e#behaviorsubject
https://qiita.com/yyokii/items/81dc182dc4a6f1b9fd1f#subject
(Publish|Behaviour)Relay
onNextのみ発生する(onErrorとonCompletedが発生しない)Subject。ある条件下で常に生き続けるストリームを作るために使える。
PublishとBehaviourの違いはSubjectと同じくキャッシュを持つかどうか。購読時の動作はSubjectと同じ。BehaviourRelayはvalueというプロパティで現在のキャッシュ値を参照できる。
ViewやViewModelのプロパティとしてよく使う。
参考:
https://qiita.com/yyokii/items/81dc182dc4a6f1b9fd1f#relay
https://qiita.com/morishin/items/fbbb71d6b49f89d33b98#relay-%E3%81%A8%E3%81%AF
https://egg-is-world.com/2018/08/11/rxswift-behaviorrelay-publishrelay/
https://tech.mercari.com/entry/2017/12/04/103247
Driver
メインスレッドで通知され、Hot変換され、onErrorを通知しないObservable。いきなりHotとか出てきたけど、UI部品の更新で使うとおぼえておけばOK。
ViewModelでViewの状態を表すプロパティとして使われる。それ以外にも上の性質を満たしたいときには使える。
参考:
https://qiita.com/k5n/items/44ef2ab400f47fb66731
https://qiita.com/summer-hues/items/e8786cd75c292cdc3ec0
https://qiita.com/inamiy/items/d6fa90d0401fa0e83852
Single
一度のみ値(onSuccess)かエラー(onError)が流れるObservable。
API通信など、1回で完結する処理に使える。
参考:
https://qiita.com/yyokii/items/81dc182dc4a6f1b9fd1f#single
https://qiita.com/monoqlo/items/7bcec98432389b3b8909
https://qiita.com/shoheiyokoyama/items/40b9a2e9f8ae747c477a#single
Completable
onCompletedかonErrorのみが流れるObservable。
返り値を使わないAPIなど、完了したことだけが知りたい場合に使える。
参考:
https://qiita.com/yyokii/items/81dc182dc4a6f1b9fd1f#completable
https://qiita.com/monoqlo/items/7bcec98432389b3b8909
https://qiita.com/shoheiyokoyama/items/40b9a2e9f8ae747c477a#completable
Observableの購読/購読解除
Observableを購読することで、イベントが流れるようになります。購読時にイベントが流れてきたときの処理を書きます。
基本的にはObservableに対してsubscribe()
を使います。派生によって流れてくるイベントが違うので、それに合わせてイベントが流れてきたときの処理を記述します。
bind(to:)
を使うと、あるObservableを別のObservableに接続させることができます。ViewControllerのサブビューで発生したイベントを、ViewControllerを通してViewModelに通知したい場合などに使用できます。
購読したいのがDriverの場合は、drive()
を使用します。
ずっと購読しているとメモリリークしてしまうので、必要がなくなったら購読を解除します。
それぞれのObservableに対してdispose()
を呼ぶことで購読を解除できます。
ただ、これは非常に面倒なので、DisposeBagを使用してまとめて購読を解除することもできます。画面が消えるときにその画面で購読したものを一斉に解除する、といった使い方をします。
参考:
https://qiita.com/_ha1f/items/e16ddc6017c4ad807c3c#subscribe%E8%B3%BC%E8%AA%AD
https://qiita.com/_ha1f/items/e16ddc6017c4ad807c3c#dispose%E8%B3%BC%E8%AA%AD%E8%A7%A3%E9%99%A4
https://qiita.com/k5n/items/17f845a75cce6b737d1e#observable
Observableの生成
様々な生成関数があります。
と言っても個人的にはcreate()
くらいしか使わないですが、知っておくといざというときにきっと役に立ちます。
参考:
https://qiita.com/_ha1f/items/43b28792d27dbee7133d
https://qiita.com/moaible/items/de94c574b25ea4f0ef17
イベントの加工と合成
イベントで流れていた値を加工して使いたい場合があります。例えばViewからViewModelに対してIndexPathが流れてきて、それを元にデータを取得する場合など。
その際に使用できるオペレータと呼ばれるものがたくさん用意されています。(オペレータ自体はとても広い概念で、例えばObservableの生成関数もオペレータの一種です)
加工するものとしてはfilter、map、flatMap、skipなどがあります。
また合成ではmerge、zip、concat、combineLatestなどがあります。
とにかくたくさんありますが、それぞれどのような動きをするのかを把握しましょう。
参考:
https://qiita.com/_ha1f/items/db72471d0c9e82fab13d#_reference-dfb07cedc63c1f933f92
https://qiita.com/k5n/items/17f845a75cce6b737d1e#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E5%8A%A0%E5%B7%A5
https://qiita.com/k5n/items/17f845a75cce6b737d1e#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E5%90%88%E6%88%90
https://qiita.com/atizawa/items/92dfa5f49546b4711957
https://qiita.com/k5n/items/e80ab6bff4bbb170122d
HotとCold
Rxをやっているとそのうちこの話題にたどり着きます。
ColdなObservableはsubscribeされて初めてイベントが流れ出し、subscribeされる度に別々のストリームが生成されます。
対してHotなObservableはsubscribeされてなくてもイベントが流れ、一つのストリームを複数のObserverがsubscribeできます。
最初はなんのこっちゃですが、ここを理解してないと効率の悪いコードを書いてしまったり、期待する結果にならなかったりするので、うまくいかないときは戻ってきて確認しましょう。
参考:
https://qiita.com/morishin/items/99c0493de58079b722fe#cold--hot-
https://qiita.com/_ha1f/items/2d0fc50505ce3a1fcdab
https://qiita.com/toRisouP/items/f6088963037bfda658d3
https://qiita.com/kazu0620/items/bde4a65e82a10bd33f88
サンプルを読む
ここまで来ればRxについてはなんとなく読めるはずなので、サンプルを読み漁って実際にどんな使われたかをしているか見てみましょう。
まずはRxSwiftについてるサンプルを読んでみるのが良いと思います。解説記事もあるので読みやすいかと思います。
参考:
https://qiita.com/kzykbys/items/3bbb4fa24d30917afd05
https://qiita.com/yuzushioh/items/634a96c6fd69de4acdd0
https://qiita.com/fumiyasac@github/items/90d1ebaa0cd8c4558d96
https://qiita.com/kumapo/items/6207d74be19cedcf4b72
などなど
便利なライブラリなど
RxViewController
UIViewControllerにrxを生やして、ライフサイクルのイベントをRx風に扱えるようにしてくれます。
NSObject-Rx
NSObjectのextensionでDisposeBagを追加することで、各クラスで毎回DisposeBagを書かなくて済むようにしてくれます。
Unio
MVVMのためのフレームワークです。MVの入出力を非常にわかりやすくしてくれますし、フレームワークを導入することで、個人による記述のブレも小さくすることができます。
おわりに
Rxの記事でコードが少しも出てこないというのはなかなか挑戦的だと思いましたが、勉強トピックのカタログというイメージで書きました
だいぶ粗い内容だし、リンクだらけですが、参考になれば幸いです