Slide 1

Slide 1 text

Extension functions for LiveData 2020.08.21 YUMEMI.apk #1 Hiroyuki Kusu ( @hkusu_ )

Slide 2

Slide 2 text

About me

Slide 3

Slide 3 text

https://speakerdeck.com/hkusu/unit-test-for-viewmodel-and-livedata ࠓճ͸ ↑ ʹ࣌ؒతʹؚΊΔ͜ͱ͕ग़དྷͳ͔ͬͨ࿩୊

Slide 4

Slide 4 text

ViewModel தͷ LiveData ͷهड़ private val _liveData1: MutableLiveData = MutableLiveData() val liveData1: LiveData = _liveData1 val liveData2: LiveData = liveData1.map { // ... } "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" • σʔλͷؔ࿈ੑ͕એݴతʹఆٛ͞Ε͍ͯΔͱ෼͔Γ΍͍͢ • ΋ͬͱศརʹ͍ͨ͠..! • ࣗલͰ࡞੒֦ͨ͠ுؔ਺Λɺ͍͔ͭ͘঺հ͠·͢

Slide 5

Slide 5 text

fun LiveData.coMap( coroutineScope: CoroutineScope, coroutineContext: CoroutineContext = coroutineScope.coroutineContext, block: suspend (T) -> U ): LiveData { val result = MediatorLiveData() result.addSource(this) { coroutineScope.launch(coroutineContext) { result.postValue(block.invoke(it)) } } return result } ※ switchMap() ͱ livedata {} Λ૊Έ߹Θͤͯ΋ಉ͜͡ͱ͕Ͱ͖·͢ ※ ݱঢ়Ͱ͸ inline fun ʹ͢ΔͱམͪΔ https://github.com/Kotlin/kotlinx.coroutines/issues/ coMap val liveData2: LiveData> = liveData1.coMap(viewModelScope) { it: Int delay(3_000) sampleRepository.getItemList(it) // suspending function } ར༻Πϝʔδ ؔ਺ͷఆٛ

Slide 6

Slide 6 text

val liveData3: LiveData = merge(liveData1, liveData2) { i: Int?, b: Boolean? -> // ... } inline fun merge( liveData1: LiveData, liveData2: LiveData, crossinline block: (T1?, T2?) -> U? ): LiveData { val result = MediatorLiveData() result.addSource(liveData1) { t1 -> block.invoke(t1, liveData2.value)?.takeUnless { it is Unit }?.let { result.value = it } } result.addSource(liveData2) { t2 -> block.invoke(liveData1.value, t2)?.takeUnless { it is Unit }?.let { result.value = it } } return result } merge ར༻Πϝʔδ ؔ਺ͷఆٛ (֦ுؔ਺Ͱͳ͘ී௨ͷؔ਺) block தͰ LiveDataͷߋ৽ΛΩϟϯηϧͰ͖ΔΑ͏ʹ͢Δҝͷॲஔ (Ωϟϯηϧ͍ͨ͠৔߹͸null΋͘͠͸໭Γ஋ͳ͠Ͱreturn͢Δ)

Slide 7

Slide 7 text

val liveData2: LiveData = liveData1.filter { 100 < it } inline fun LiveData.filter( crossinline block: (T) -> Boolean ): LiveData { val result = MediatorLiveData() result.addSource(this) { if (block.invoke(it)) { result.value = it } } return result } filter ར༻Πϝʔδ ؔ਺ͷఆٛ

Slide 8

Slide 8 text

val liveData2: LiveData = liveData1.map { // ... }.coMap(viewModelScope) { // ... }.map { // ... }.coMap(viewModelScope) { // ... }.filter { // ... } ͭͳ͛ͯॻ͚Δ

Slide 9

Slide 9 text

͋ΔLiveDataͷมߋΛܖػʹͳʹ͔͠ΒͷॲཧΛ͍͕ͨ͠.. ( LiveData ͷͭͳ͕Γ͚ͩͰ্ख͘ઃܭͰ͖ͳ͍έʔε ) val liveData2: LiveData = liveData1.map { } ແ༻ͳ LiveData2 Λఆٛͯ͠΋ɺLiveData2 ͷ Observer ͕ډͳ͍৔߹ ͜ͷϒϩοΫ͸࣮ߦ͞Εͳ͍

Slide 10

Slide 10 text

val observe = liveData1.map { }.observe() // ... override fun onCleared() { observe.cancel() } class Observe(private val liveData: LiveData) { private val observer = Observer {} fun start() { liveData.observeForever(observer) } fun cancel() { liveData.removeObserver(observer) } } fun LiveData.observe(): Observe = Observe(this).apply { start() } observe ViewModel ͷഁغ࣌ʹ͸؍ଌΛऴྃͤ͞Δඞཁ observeForever Λར༻ ͜ͷϒϩοΫͰ೚ҙͷॲཧ ؔ਺ͷఆٛ ར༻Πϝʔδ

Slide 11

Slide 11 text

https://gist.github.com/hkusu/b258c4e4ef488ccd8b213586417ffbec ࠓճɺ঺հ֦ͨ͠ுؔ਺͸gistʹஔ͖·ͨ͠

Slide 12

Slide 12 text

Thank you ! @hkusu_