【MVVM】Transformations.switchMap() の使い方

👉 ViewModels and LiveData: Patterns + AntiPatterns – Android Developers – Medium 
👉 LiveData beyond the ViewModel — Reactive patterns using Transformations and MediatorLiveData 

LiveData をリアクティブに操作できるこのユーティリティメソッド。

LiveData を変換します。
これから返されるLiveData オブジェクトを Observe しておけば、Observer のライフサイクルを考慮しながら、データを変換することができます。lazy に処理され、呼び出しや依存関係の記述なしに、ライフサイクル関連の動作が引き継がれます。

👉 Transformations | android.arch.lifecycle.Transformations  |  Android Developers 

実体は MediatorLiveData のユーティリティ

ソースコードを見てみます。


@SuppressWarnings("WeakerAccess")
public class Transformations {

    private Transformations() {
    }

    @MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

    @MainThread
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
}

👉 Cross Reference: Transformations.java 

ソースコードより、これは、1:1 の MediatorLiveData を使うための便利ツールです。


class MainViewModel {
  val repositoryResult = Transformations.switchMap(userManager.user) { user →
     repository.getDataForUser(user)
  }
}

複数に変換したい場合は、直接 MediatorLiveData を使うのが良いでしょう。


val liveData1: LiveData<Int> = ...
val liveData2: LiveData<Int> = ...

val result = MediatorLiveData<Int>()

result.addSource(liveData1) { value →
    result.setValue(value)
}
result.addSource(liveData2) { value →
    result.setValue(value)
}

まとめ

ViewModel 内で、その下にある Repository との間で使います。

View (Activity / Fragment) のライフサイクルにも考慮されています。

ViewModel内で、データの id からその詳細情報の取得をしたり、検索文字列をフィルターとしてデータ抽出したり、などに使うことが多いようです。

👉 MediatorLiveDataとTransformationsでViewModelを効果的に使う