今回はリアクティブプログラミングについてまとめようと思います
RxSwiftをベースに説明しますが、概念の部分はAndroidや他のプログラミング言語でリアクティブプログラミングを行う場合でも共通すると思います
Rxとは?
Rx(Reactive X)とは、「オブザーバパターン」「イテレータパターン」「関数型プログラミング」の概念を実装している機能です
Rxを導入するメリットは、「値の変化を検知できる」「非同期の処理を簡潔に書ける」です
値の変化というのは変数の変化やUIの変化も含まれます(ボタンのタッチなども含める)
Observerパターン
Rx全般において、処理の流れの基本的な設計として用いられるものの1つに、Observerパターンと呼ばれるデザインパターンがあります
Observerパターンとは、あるオブジェクトで発生したイベントを別のオブジェクト(オブザーバー)へ通知する処理で用いられるデザインパターンの1つです
Rx関連でのイベントとは、画面上で発生するタップイベントの他に、変数の値が変わった・APIから値が取得できたなどプログラム内におけるオブジェクトの状態の変化全てを指します
RxSwiftでは、イベントストリームの中で流れて来るデータの部分をデータストリームと言われています。Observerパターンでデータストリームを扱うクラスには、次の2つのクラスがあります
- Observable:監視可能なものを表すクラス
- Observer:Observableからデータストリームを受け取るクラス
Observable
監視可能なものを表すクラスで通知を行うことができます。一方、Observer
は監視を行う側です。
Observableを「出版」、Observerを「購読」と呼ぶ場合もあります。それは次のような由来があります
出版社が事業を始め、定期的な刊行物を発行します。購読者は、出版社に購読の申し込みをすると定期的に刊行物が届けられます。 購読を解除すると、解除日以降は刊行物は届けられません。 出版者が新聞や雑誌などの定期刊行物を発行し、それを誰でも好きなタイミングで購読したり辞めたりできることと同じように考えるものです。
用語説明
Observable
Observerパターンで説明したObserver
はイベントを流せるクラスで、Observable
はイベントを受け取れるクラスです
RxSwiftでのイベントは次の3種類があります
イベントの種別 | 発生するタイミング | 渡される値 |
---|---|---|
next(Element) | イベントが発生した際に何度でも | イベント派生元の値(データストリーム) |
error | エラーが発生した際に一度だけ | エラー情報 |
completed | 処理が完了した際に一度だけ | なし |
Observableはイベントを受け取れるのですが、イベントを受け取る際にはsubscribe(_:)メソッドを利用します。次のソースコードはRxSwiftの一例です
let disposable = textField.rx.text.asObservable() .subscribe( onNext: { value in // 通常イベント発生時の処理 }, onError: { error in // エラー発生時の処理 }, onCompleted: { // 完了時の処理 } )
textField.rx.text.asObservable()
が Observableです。Observableが値が更新されたというイベントを受け取って、Observerはイベントに対して登録された処理を行います(今回だとonNext: { value in }など)
先程のソースコードはtextField.rx.text.asObservable().subscribe()
まではObservableで、onNext・onError・onCompletedの処理はObserverです(Observerはイベントを流せます)
Operator
RxにはObservableで作ったストリームを操作する機能が提供されています
ストリーム(Observable)を変換・合成・生成などの操作をオペレーターと言います
オペレーターにはmap
やfilter
など多くのものがあります
いくつかみていこうと思います。まずはmapです。mapは流れてきた値を変換するために使います。 下記例では入力された Int 型の値を 10倍にしています
_ = Observable.of(1,2,3) .map { $0 * 10 } .subscribe(onNext: { print("onNext: ", $0) }) // 出力結果 onNext: 10 onNext: 20 onNext: 30
次はfilterです。filterは流れてきた値で条件に合うものだけを残すために使います。 下記例では4で割り切れる数字のみを残しています
= Observable.of(1, 2, 3, 4, 5) .filter { $0 % 4 == 0 } .subscribe(onNext: { print($0) // => 4 })
そして、flatMap
についてです。flatMapは今まで触れたmapやfilterとは違いがあります。それは、新たなObservableを発行するかどうかです
flatMapは元のObservableのイベント毎に、Observableに変換して、そのObservableが発行するイベントを1つに集約します
_ = Observable.of(1,2,3) .flatMap { number in // mapとは違い新たにObservableを作成する Observable.of("\(number)" + "a", "\(number)" + "b", "\(number)" + "c") } .subscribe(onNext: { print("onNext: ", $0) }) // 出力結果(非同期なので毎回この順番という保証はありません) onNext: 1a onNext: 1b onNext: 2a <- 非同期に処理が行われる onNext: 1c onNext: 2b onNext: 3a onNext: 2c onNext: 3b onNext: 3c
上記の例をみるとわかるようにflatMapでは新たにObservableを作成しています。マーブルダイアグラムでは次のようになります
flatMapは非同期で受け取る値の順番は毎回変わる可能性があります。APIの結果を受け取る場合にはflatMapLatest
という常に一番新しいObservableを実行するオペレーターを使うようにしましょう
Subject
RxSwiftでのSubject は、ObservableとObserver両方の機能を持っています(イベントを流すこともでき、イベントを受け取ることもできる)
Observableでもsubscribeすると、onNext・onError・onCompletedの処理が行われますが、流れてくる値を受け取るための手段でObservable自らイベントを発せさせるわけではありません
Subjectは自身でイベントを流すことができます。次の例をみてください
let subject = PublishSubject<String>() let _ = subject.subscribe(onNext: { print("onNext:", $0) }, onCompleted: { }) // イベントを流す subject.onNext("A") subject.onNext("B") subject.onNext("C") subject.onNext("D") subject.onCompleted()
Observableクラスではsubject.onNext("A")
などイベントを流すことはできませんが、Subjectだと可能です
Subjectでよく使うのは次の種類があります
種類 | 動作 |
---|---|
PublishSubject | キャッシュせず、来たイベントをそのまま通知する |
ReplaySubject | 指定した値だけキャッシュを持ち、subscribe時に直近のキャッシュしたものを通知する |
BehaviorSubject | 初期値を持つことができ、1つだけキャッシュを持ち、subscribe時に直近のキャッシュしたものを通知する(初期値をsubscribeで受け取れる) |
HotとCold
ObservableはHot
とCold
の2種類の性質があります
ColdなObservableはsubscribeされるまで動作せず、複数subscribeした場合にはそれぞれに対してObservableが複製されてしまいます。 なので、同じ値が複数回流れることになります。ほとんどのOpertorがColdなObservableです
Observable.of("event") .map { text -> String in print(text) return text } // subscribeしていないので、何も出力されない
HotなObservableはsubscribeされなくても動作し、複数subscribeしているのにも関わらず、1つのObservableで同じ値がそれぞれに流れます
SubjectはHotで、mapなどのオペレーターは内部でObservableを返しますがColdなObservableを返すので、ColdなObservableです
Rxのストリームは基本的にSubscribeされた瞬間に各オペレータの動作が始まるようになっています。 ですがHot Observableをストリームの途中に挟むことで、Subscribeを実行するより前にストリームを稼働させることができます。
また、Hot Observableはストリームを分岐することができます。そして、分岐したストリームそれぞれに同一の値を渡します
最後に
大まかな説明になってしまいましたが、概念を理解することでCombineなどに活かせると思います。 また、iOS以外でのプログラミングでもリアクティブプログラミングという考え方は使うので知っておくとで他の場面で活用できる可能性もあるのでぜひ学習してみてください!