[Android]LiveDataでRxのCombineLatestをやる

2019-06-22

複数の LiveData を組み合わせようとすると MediatorLiveData を使用することになりますが、
これがボイラープレート山盛りなコードを書かされて非常に使いにくい。

そこで、Rx みたいにお手軽に 2 つの LiveData を組み合わせる方法はないかと探して、ちょうど良さそうな実装が書かれた下記の記事を見つけました。

Combine Results from Multiple Async Requests – Gaurav Goyal – Medium

この記事では、 zip〜 と関数名をつけていますが、Rx 的には zip  ではないです。

2 つのストリーム、どちらかに更新があれば、それぞれストリームの最後の値のセットが流れてくるので、Rx 的には Zip でなく、CombineLatest になります。

ReactiveX – Zip operator
ReactiveX – CombineLatest operator

この記事では関数として定義してますが、もっと Rx 的に書けるように LiveData の拡張関数として書いてみました。

fun <A, B> LiveData<A>.combineLatest(b: LiveData<B>): LiveData<Pair<A, B>> {
    return MediatorLiveData<Pair<A, B>>().apply {
        var lastA: A? = null
        var lastB: B? = null

        fun update() {
            val localLastA = lastA
            val localLastB = lastB
            if (localLastA != null && localLastB != null)
                value = Pair(localLastA, localLastB)
        }

        addSource(this@combineLatest) {
            lastA = it
            update()
        }
        addSource(b) {
            lastB = it
            update()
        }
    }
}

これで、LiveData だけで、ログイン画面で ID とパスワードの入力チェックが通ったらログインボタンを有効にする、というような処理もサクッとかけるようになります。