Skip to content

Instantly share code, notes, and snippets.

@afollestad
Created December 7, 2018 06:55
Show Gist options
  • Select an option

  • Save afollestad/2082541041403b504bdf9d287aa4b6fe to your computer and use it in GitHub Desktop.

Select an option

Save afollestad/2082541041403b504bdf9d287aa4b6fe to your computer and use it in GitHub Desktop.
Merge two live data into one
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
typealias Zipper<T, K, R> = (T, K) -> R
/** @author Aidan Follestad (@afollestad) */
class ZipLiveData<T, K, R>(
source1: LiveData<T>,
source2: LiveData<K>,
private val distinctUntilChanged: Boolean,
private val resetAfterEmission: Boolean,
private val zipper: Zipper<T, K, R>
) : MediatorLiveData<R>() {
private var data1: T? = null
private var data2: K? = null
private var lastNotified: R? = null
init {
super.addSource(source1) {
if (data1 == it) return@addSource
data1 = it
maybeNotify()
}
super.addSource(source2) {
if (data2 == it) return@addSource
data2 = it
maybeNotify()
}
}
private fun maybeNotify() {
if (data1 != null && data2 != null) {
val zippedUp = zipper(data1!!, data2!!)
if (!distinctUntilChanged || zippedUp != lastNotified) {
value = zippedUp
lastNotified = zippedUp
if (resetAfterEmission) {
data1 = null
data2 = null
}
}
}
}
override fun <S : Any?> addSource(
source: LiveData<S>,
onChanged: Observer<in S>
) {
throw UnsupportedOperationException()
}
override fun <T : Any?> removeSource(toRemote: LiveData<T>) {
throw UnsupportedOperationException()
}
}
fun <T, K, R> zip(
source1: LiveData<T>,
source2: LiveData<K>,
distinctUntilChanged: Boolean = true,
resetAfterEmission: Boolean = false,
zipper: Zipper<T, K, R>
) = ZipLiveData(
source1 = source1,
source2 = source2,
distinctUntilChanged = distinctUntilChanged,
resetAfterEmission = resetAfterEmission,
zipper = zipper
)
fun <T, K> zip(
source1: LiveData<T>,
source2: LiveData<K>,
distinctUntilChanged: Boolean = true,
resetAfterEmission: Boolean = false
) = zip(
source1 = source1,
source2 = source2,
distinctUntilChanged = distinctUntilChanged,
resetAfterEmission = resetAfterEmission,
zipper = { left, right -> Pair(left, right) })
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.MutableLiveData
import org.junit.Rule
import org.junit.Test
/** @author Aidan Follestad (@afollestad) */
class ZipTest {
@Rule @JvmField val rule = InstantTaskExecutorRule()
@Test fun test_withDistinct() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, true)
.test()
data1.postValue("Hello")
data2.postValue(24)
zipped.assertValues(Pair("Hello", 24))
data1.postValue("Hello")
data2.postValue(24)
zipped.assertNoValues()
}
@Test fun test_noDistinct() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, false)
.test()
data1.value = "Hello"
data2.value = 24
zipped.assertValues(Pair("Hello", 24))
data1.value = "Hi"
data2.value = 24
zipped.assertValues(Pair("Hi", 24))
}
@Test fun test_noDistinct_resetAfterEmission() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, false, true)
.test()
data1.value = "Hello"
data2.value = 24
zipped.assertValues(Pair("Hello", 24))
data1.value = "Hi"
data2.value = 50
zipped.assertValues(Pair("Hi", 50))
}
@Test fun test_withDistinct_customZipper() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, true,
zipper = { left, right ->
"$left $right"
}).test()
data1.value = "Hello"
data2.value = 24
zipped.assertValues("Hello 24")
data1.value = "Hello"
data2.value = 24
zipped.assertNoValues()
}
@Test fun test_noDistinct_customZipper() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, false,
zipper = { left, right ->
"$left $right"
}).test()
data1.value = "Hello"
data2.value = 24
zipped.assertValues("Hello 24")
data1.value = "Hi"
data2.value = 24
zipped.assertValues("Hi 24")
}
@Test fun test_noDistinct_customZipper_resetAfterEmission() {
val data1 = MutableLiveData<String>()
val data2 = MutableLiveData<Int>()
val zipped = zip(data1, data2, false, true,
zipper = { left, right ->
"$left $right"
}).test()
data1.value = "Hello"
data2.value = 24
zipped.assertValues("Hello 24")
data1.value = "Hi"
data2.value = 50
zipped.assertValues("Hi 50")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment