Skip to content

Instantly share code, notes, and snippets.

@hkusu
Last active October 8, 2020 07:14
Show Gist options
  • Save hkusu/6bdc35e75614df559135174535e3c3ce to your computer and use it in GitHub Desktop.
Save hkusu/6bdc35e75614df559135174535e3c3ce to your computer and use it in GitHub Desktop.
旧版の実装は https://gist.github.com/hkusu/b258c4e4ef488ccd8b213586417ffbec を参照。このコードは Ktolin 1.4.0、Coroutines 1.3.9 で動作確認しています。
package io.github.hkusu.sample.ext
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
inline fun <T, U> LiveData<T>.mapNotNull(
crossinline block: (T) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(this) { t ->
block.invoke(t)?.let { result.value = it } // block が null を返した場合は何もしない
}
return result
}
// 現状では inline fun とすると落ちる https://github.com/Kotlin/kotlinx.coroutines/issues/1150
fun <T, U> LiveData<T>.coMap(
coroutineScope: CoroutineScope, // ViewModel であれば viewModelScope を渡す
coroutineContext: CoroutineContext = coroutineScope.coroutineContext, // block 中で withContext で切り替えてもOK
block: suspend (T) -> U
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(this) {
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(it))
}
}
return result
}
fun <T, U> LiveData<T>.coMapNotNull(
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(this) { t ->
coroutineScope.launch(coroutineContext) {
block.invoke(t)?.let { result.postValue(it) }
}
}
return result
}
inline fun <T1, T2, U> merge(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
crossinline block: (T1?, T2?) -> U
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
result.value = block.invoke(t1, liveData2.value)
}
result.addSource(liveData2) { t2 ->
result.value = block.invoke(liveData1.value, t2)
}
return result
}
inline fun <T1, T2, T3, U> merge(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
liveData3: LiveData<T3>,
crossinline block: (T1?, T2?, T3?) -> U
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
result.value = block.invoke(t1, liveData2.value, liveData3.value)
}
result.addSource(liveData2) { t2 ->
result.value = block.invoke(liveData1.value, t2, liveData3.value)
}
result.addSource(liveData3) { t3 ->
result.value = block.invoke(liveData1.value, liveData2.value, t3)
}
return result
}
inline fun <T1, T2, U> mergeNotNull(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
crossinline block: (T1?, T2?) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
block.invoke(t1, liveData2.value)?.let { result.value = it }
}
result.addSource(liveData2) { t2 ->
block.invoke(liveData1.value, t2)?.let { result.value = it }
}
return result
}
inline fun <T1, T2, T3, U> mergeNotNull(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
liveData3: LiveData<T3>,
crossinline block: (T1?, T2?, T3?) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
block.invoke(t1, liveData2.value, liveData3.value)?.let { result.value = it }
}
result.addSource(liveData2) { t2 ->
block.invoke(liveData1.value, t2, liveData3.value)?.let { result.value = it }
}
result.addSource(liveData3) { t3 ->
block.invoke(liveData1.value, liveData2.value, t3)?.let { result.value = it }
}
return result
}
fun <T1, T2, U> coMerge(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T1?, T2?) -> U
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(t1, liveData2.value))
}
}
result.addSource(liveData2) { t2 ->
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(liveData1.value, t2))
}
}
return result
}
fun <T1, T2, T3, U> coMerge(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
liveData3: LiveData<T3>,
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T1?, T2?, T3?) -> U
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(t1, liveData2.value, liveData3.value))
}
}
result.addSource(liveData2) { t2 ->
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(liveData1.value, t2, liveData3.value))
}
}
result.addSource(liveData3) { t3 ->
coroutineScope.launch(coroutineContext) {
result.postValue(block.invoke(liveData1.value, liveData2.value, t3))
}
}
return result
}
fun <T1, T2, U> coMergeNotNull(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T1?, T2?) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
coroutineScope.launch(coroutineContext) {
block.invoke(t1, liveData2.value)?.let { result.postValue(it) }
}
}
result.addSource(liveData2) { t2 ->
coroutineScope.launch(coroutineContext) {
block.invoke(liveData1.value, t2)?.let { result.postValue(it) }
}
}
return result
}
fun <T1, T2, T3, U> coMergeNotNull(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
liveData3: LiveData<T3>,
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T1?, T2?, T3?) -> U?
): LiveData<U> {
val result = MediatorLiveData<U>()
result.addSource(liveData1) { t1 ->
coroutineScope.launch(coroutineContext) {
block.invoke(t1, liveData2.value, liveData3.value)?.let { result.postValue(it) }
}
}
result.addSource(liveData2) { t2 ->
coroutineScope.launch(coroutineContext) {
block.invoke(liveData1.value, t2, liveData3.value)?.let { result.postValue(it) }
}
}
result.addSource(liveData3) { t3 ->
coroutineScope.launch(coroutineContext) {
block.invoke(liveData1.value, liveData2.value, t3)?.let { result.postValue(it) }
}
}
return result
}
inline fun <T> LiveData<T>.takeIf(
crossinline block: (T) -> Boolean
): LiveData<T> {
val result = MediatorLiveData<T>()
result.addSource(this) {
if (block.invoke(it)) {
result.value = it
}
}
return result
}
fun <T> LiveData<T>.coTakeIf(
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T) -> Boolean
): LiveData<T> {
val result = MediatorLiveData<T>()
result.addSource(this) {
coroutineScope.launch(coroutineContext) {
if (block.invoke(it)) {
result.postValue(it)
}
}
}
return result
}
inline fun <T> LiveData<T>.takeUnless(
crossinline block: (T) -> Boolean
): LiveData<T> {
val result = MediatorLiveData<T>()
result.addSource(this) {
if (!block.invoke(it)) {
result.value = it
}
}
return result
}
fun <T> LiveData<T>.coTakeUnless(
coroutineScope: CoroutineScope,
coroutineContext: CoroutineContext = coroutineScope.coroutineContext,
block: suspend (T) -> Boolean
): LiveData<T> {
val result = MediatorLiveData<T>()
result.addSource(this) {
coroutineScope.launch(coroutineContext) {
if (!block.invoke(it)) {
result.postValue(it)
}
}
}
return result
}
class Observe<T>(private val liveData: LiveData<T>) {
private val observer = Observer<T> {}
fun start() {
liveData.observeForever(observer)
}
fun cancel() {
liveData.removeObserver(observer)
}
}
fun <T> LiveData<T>.observe(): Observe<T> = Observe(this).apply { start() }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment