Skip to content Skip to sidebar Skip to footer

How To Combine Two Live Data One After The Other?

I have next use case: User comes to registration form, enters name, email and password and clicks on register button. After that system needs to check if email is taken or not and

Solution 1:

You can use my helper method:

val profile = MutableLiveData<ProfileData>()

val user = MutableLiveData<CurrentUser>()

val title = profile.combineWith(user) { profile, user ->
    "${profile.job}${user.name}"
}

fun<T, K, R> LiveData<T>.combineWith(
    liveData: LiveData<K>,
    block: (T?, K?) -> R
): LiveData<R> {
    val result = MediatorLiveData<R>()
    result.addSource(this) {
        result.value = block(this.value, liveData.value)
    }
    result.addSource(liveData) {
        result.value = block(this.value, liveData.value)
    }
    return result
}

Solution 2:

With the help of MediatorLiveData, you can combine results from multiple sources. Here an example of how would I combine two sources:

classCombinedLiveData<T, K, S>(source1: LiveData<T>, source2: LiveData<K>, privateval combine: (data1: T?, data2: K?) -> S) : MediatorLiveData<S>() {

    privatevar data1: T? = nullprivatevar data2: K? = nullinit {
        super.addSource(source1) {
            data1 = it
            value = combine(data1, data2)
        }
        super.addSource(source2) {
            data2 = it
            value = combine(data1, data2)
        }
    }

    overridefun<S : Any?>addSource(source: LiveData<S>, onChanged: Observer<inS>) {
        throw UnsupportedOperationException()
    }

    overridefun<T : Any?>removeSource(toRemove: LiveData<T>) {
        throw UnsupportedOperationException()
    }
}

here is the gist for above, in case it is updated on the future: https://gist.github.com/guness/0a96d80bc1fb969fa70a5448aa34c215

Solution 3:

Jose Alcérreca has probably the best answer for this:

funblogpostBoilerplateExample(newUser: String): LiveData<UserDataResult> {

    val liveData1 = userOnlineDataSource.getOnlineTime(newUser)
    val liveData2 = userCheckinsDataSource.getCheckins(newUser)

    val result = MediatorLiveData<UserDataResult>()

    result.addSource(liveData1) { value ->
        result.value = combineLatestData(liveData1, liveData2)
    }
    result.addSource(liveData2) { value ->
        result.value = combineLatestData(liveData1, liveData2)
    }
    return result
}

Solution 4:

You can define a method that would combine multiple LiveDatas using a MediatorLiveData, then expose this combined result as a tuple.

publicclassCombinedLiveData2<A, B> extendsMediatorLiveData<Pair<A, B>> {
    private A a;
    private B b;

    publicCombinedLiveData2(LiveData<A> ld1, LiveData<B> ld2) {
        setValue(Pair.create(a, b));

        addSource(ld1, (a) -> { 
             if(a != null) {
                this.a = a;
             } 
             setValue(Pair.create(a, b)); 
        });

        addSource(ld2, (b) -> { 
            if(b != null) {
                this.b = b;
            } 
            setValue(Pair.create(a, b));
        });
    }
}

If you need more values, then you can create a CombinedLiveData3<A,B,C> and expose a Triple<A,B,C> instead of the Pair, etc. Just like in https://stackoverflow.com/a/54292960/2413303 .

EDIT: hey look, I even made a library for you that does that from 2 arity up to 16: https://github.com/Zhuinden/livedata-combinetuple-kt

Solution 5:

I did an approach based on @guness answer. I found that being limited to two LiveDatas was not good. What if we want to use 3? We need to create different classes for every case. So, I created a class that handles an unlimited amount of LiveDatas.

/**
  * CombinedLiveData is a helper class to combine results from multiple LiveData sources.
  * @param liveDatas Variable number of LiveData arguments.
  * @param combine   Function reference that will be used to combine all LiveData data results.
  * @param R         The type of data returned after combining all LiveData data.
  * Usage:
  * CombinedLiveData<SomeType>(
  *     getLiveData1(),
  *     getLiveData2(),
  *     ... ,
  *     getLiveDataN()
  * ) { datas: List<Any?> ->
  *     // Use datas[0], datas[1], ..., datas[N] to return a SomeType value
  * }
  */classCombinedLiveData<R>(vararg liveDatas: LiveData<*>,
                           privateval combine: (datas: List<Any?>) -> R) : MediatorLiveData<R>() {

      privateval datas: MutableList<Any?> = MutableList(liveDatas.size) { null }

      init {
         for(i in liveDatas.indices){
             super.addSource(liveDatas[i]) {
                 datas[i] = it
                 value = combine(datas)
             }
         }
     }
 }

Post a Comment for "How To Combine Two Live Data One After The Other?"