【Swift】RxSwift勉強してみたPart4


はじめに

前回
今回は、今まで出てきた用語をもう少し深ぼって学習していきたいと思います。

解説

Observable(観測可能)

イベントを検知するためのクラス
ストリームと言われたりする
Observableが通知するイベントは以下のようなものがある。
・onNext
デフォルトのイベントをながす
イベント内に値を格納でき、何度でも呼び出せる
・onError
エラーイベント
一度だけ呼ばれ、その時点で終了
購読を破棄
・onCompleted
完了イベント
一度だけ呼ばれ、その時点で終了
購読を破棄

ObservableとObserver

Observable: イベント発生元
Observer: イベント処理
です。例えば、この以下のような感じです。

hogeObservable // Observable
    .map { $0 * 10 } // Observable
    .subscribe(onNext: {
        // Observer
    })
    .disposed(by: disposeBag)

disposed

購読を良きタイミングで破棄してメモリリークを回避するための仕組み

SubjectとRelay

イベントの検知に加えてイベント発生もできる便利なクラス
良い使われるもの

流せるイベント バッファ
PublishSubject onNext, onError, onComplete 持たない
BehaviorSubject onNext, onError, onComplete 持つ
PublishRelay onNext 持たない
BehaviorRelay onNext 持つ

バッファ

BehaviorSubject/Relayは、subscribe時に1つ過去のイベントを受け取ることができる。
最初にsubcribeするときは、宣言時に設定した初期値を受け取る。

SubjectとRelay使い分け

・Subject
通信処理やDB処理等のエラーが発生した時にその内容によって処理を分岐させたい
・Relay
UIに値をBindする

bind

Observable/Observerに対してbindメソッドを使うと指定したものにイベントストリームを接続できる
単方向のデータバインディング
subscribeして値をセットしているだけ

final class HogeViewController: UIViewController {

    @IBOutlet private weak var nameTextField: UITextField!
    @IBOutlet private weak var nameLabel: UILabel!
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        //bind利用(subscribeして値をセット)
        nameTextField.rx.text
            .bind(to: nameLabel.rx.text)
            .disposed(by: disposeBag)

        //subscribe利用
        nameTextField.rx.text
            .subscribe(onNext: { [weak self] text in
                self?.nameLabel.text = text
            })
            .disposed(by: disposeBag)

    }
}

Operator

Observableから流れてきた値をそのままsubscribe(またはbind)するのではなく、途中で値を加工してsubscribe(bind)をしたいときに使う。

概略 Operator 説明
変換 map 通常の高階関数と同じ動き
flatMap 通常の高階関数と同じ動き
reduce 通常の高階関数と同じ動き
scan reduceに似ていて、途中結果もイベント発行できる
debounce 指定時間イベントが発生しなかったら、最後に流されたイベントをながす
絞り込み filter 通常の高階関数と同じ動き
take 指定時間の間だけイベントを通知してonCompletedする
skip 指定時間の間はイベントを無視する
distinct 重複イベントを除外する
組み合わせ zip 複数のObservableを組み合わせる(異なる型でも可能)
merge 複数のObservableを組み合わせる(異なる型では不可能)
combineLatest 複数のObservableの最新値を組み合わせる(異なる型でも可能)
sample 引数にわたしたObservableのイベントが発生されたら、元のObservableの最新イベントを通知
concat 複数のObservableのイベントを順番に組み合わせる(異なる型では不可能)

map

//nameTextFieldのテキスト文字数を数えてnameLabelのテキストに反映
nameTextField.rx.text
    .map { text -> String? in
        guard let text = text else { return nil }
        return "あと\(text.count)文字"
    }
    .bind(to: nameLabel.rx.text)
    .disposed(by: disposeBag)
//ボタンをタップしたときにnameLabelにユーザーの名前を表示する
let user = User(name: "REON")
showUserNameButton.rx.tap
    .map { [weak self] in
        return self?.user.name
    }
    .bind(to: nameLabel.rx.text)
    .disposed(by: disposeBag)

filter

//整数が流れるObservableから偶数のイベントのみに絞り込んでevenObservableに流す
numberSubject
    .filter{ $0 * 2 == 0 }
    .bind(to: evenSubject)
    .disposed(by: disposeBag)

zip

//複数のAPIにリクエストして同時に反映したい場合に使える
Observable.zip(firstApiObservable, secondApiObservable)
    .subscribe(onNext: { (firstApi, secondApi) in
        // ...
    })
    .disposed(by: disposeBag)

HotとColdなObservable

HotなObservable

・subscribeされなくても動作する
・複数の箇所でsubscribeした時に全てのObservableで同じイベントが同時に流れる

ColdなObservable

・subscribeした時に動作する
・単体では意味がない
・複数の箇所でsubscribeしたとき、それぞれのObservableでそれぞれのイベントが流れる
・使い所は非同期通信処理

おわりに

次回
RxSwift楽しい!