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 n
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
}
I did an approach based on @guness answer. I found that being limited to two LiveData
s 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 LiveData
s.
/**
* 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
* }
*/
class CombinedLiveData<R>(vararg liveDatas: LiveData<*>,
private val combine: (datas: List<Any?>) -> R) : MediatorLiveData<R>() {
private val datas: MutableList<Any?> = MutableList(liveDatas.size) { null }
init {
for(i in liveDatas.indices){
super.addSource(liveDatas[i]) {
datas[i] = it
value = combine(datas)
}
}
}
}
if you want both value not null
fun <T, V, R> LiveData<T>.combineWithNotNull(
liveData: LiveData<V>,
block: (T, V) -> R
): LiveData<R> {
val result = MediatorLiveData<R>()
result.addSource(this) {
this.value?.let { first ->
liveData.value?.let { second ->
result.value = block(first, second)
}
}
}
result.addSource(liveData) {
this.value?.let { first ->
liveData.value?.let { second ->
result.value = block(first, second)
}
}
}
return result
}
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
Jose Alcérreca has probably the best answer for this:
fun blogpostBoilerplateExample(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
}
With the help of MediatorLiveData, you can combine results from multiple sources. Here an example of how would I combine two sources:
class CombinedLiveData<T, K, S>(source1: LiveData<T>, source2: LiveData<K>, private val combine: (data1: T?, data2: K?) -> S) : MediatorLiveData<S>() {
private var data1: T? = null
private var data2: K? = null
init {
super.addSource(source1) {
data1 = it
value = combine(data1, data2)
}
super.addSource(source2) {
data2 = it
value = combine(data1, data2)
}
}
override fun <T : Any?> addSource(source: LiveData<T>, onChanged: Observer<T>) {
throw UnsupportedOperationException()
}
override fun <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