-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separate IntentDelegate, InternalChangesDelegate and EventDelegate fr…
…om BaseViewModel to prefer delegation over inheritance
- Loading branch information
1 parent
da4698c
commit a310d86
Showing
14 changed files
with
168 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 0 additions & 93 deletions
93
core/src/main/java/eu/krzdabrowski/starter/core/BaseViewModel.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/BaseViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import android.os.Parcelable | ||
import androidx.lifecycle.SavedStateHandle | ||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.catch | ||
import kotlinx.coroutines.flow.merge | ||
import kotlinx.coroutines.flow.scan | ||
import kotlinx.coroutines.launch | ||
import timber.log.Timber | ||
|
||
private const val SAVED_UI_STATE_KEY = "savedUiStateKey" | ||
|
||
abstract class BaseViewModel<UI_STATE : Parcelable, PARTIAL_UI_STATE, EVENT, INTENT>( | ||
savedStateHandle: SavedStateHandle, | ||
initialState: UI_STATE, | ||
) : ViewModel(), | ||
IntentDelegate<INTENT, PARTIAL_UI_STATE> by IntentDelegateImpl(), | ||
InternalChangesDelegate<PARTIAL_UI_STATE> by InternalChangesDelegateImpl(), | ||
EventDelegate<EVENT> by EventDelegateImpl() { | ||
|
||
val uiState = savedStateHandle.getStateFlow( | ||
key = SAVED_UI_STATE_KEY, | ||
initialValue = initialState, | ||
) | ||
|
||
init { | ||
viewModelScope.launch { | ||
merge( | ||
getIntents(::mapIntents), | ||
getInternalChanges(), | ||
) | ||
.scan(uiState.value, ::reduceUiState) | ||
.catch { Timber.e(it) } | ||
.collect { | ||
savedStateHandle[SAVED_UI_STATE_KEY] = it | ||
} | ||
} | ||
} | ||
|
||
fun acceptIntent(intent: INTENT) { | ||
viewModelScope.launch { | ||
setIntent(intent) | ||
} | ||
} | ||
|
||
protected fun acceptChanges(vararg internalChangesFlows: Flow<PARTIAL_UI_STATE>) { | ||
viewModelScope.launch { | ||
setInternalChanges(*internalChangesFlows) | ||
} | ||
} | ||
|
||
protected abstract fun mapIntents(intent: INTENT): Flow<PARTIAL_UI_STATE> | ||
|
||
protected abstract fun reduceUiState( | ||
previousState: UI_STATE, | ||
partialState: PARTIAL_UI_STATE, | ||
): UI_STATE | ||
} |
8 changes: 8 additions & 0 deletions
8
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/EventDelegate.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface EventDelegate<EVENT> { | ||
fun getEvents(): Flow<EVENT> | ||
suspend fun setEvent(event: EVENT) | ||
} |
16 changes: 16 additions & 0 deletions
16
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/EventDelegateImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import kotlinx.coroutines.channels.Channel | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.receiveAsFlow | ||
|
||
class EventDelegateImpl<EVENT> : EventDelegate<EVENT> { | ||
|
||
private val eventChannel = Channel<EVENT>(Channel.BUFFERED) | ||
|
||
override fun getEvents(): Flow<EVENT> = eventChannel.receiveAsFlow() | ||
|
||
override suspend fun setEvent(event: EVENT) { | ||
eventChannel.send(event) | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/IntentDelegate.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface IntentDelegate<INTENT, PARTIAL_UI_STATE> { | ||
fun getIntents( | ||
mapOperation: (INTENT) -> Flow<PARTIAL_UI_STATE>, | ||
): Flow<PARTIAL_UI_STATE> | ||
|
||
suspend fun setIntent(intent: INTENT) | ||
} |
26 changes: 26 additions & 0 deletions
26
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/IntentDelegateImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import eu.krzdabrowski.starter.core.coroutines.flatMapConcurrently | ||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.MutableSharedFlow | ||
import kotlinx.coroutines.flow.onSubscription | ||
|
||
class IntentDelegateImpl<INTENT, PARTIAL_UI_STATE> : IntentDelegate<INTENT, PARTIAL_UI_STATE> { | ||
|
||
private val intentsFlowListenerStarted = CompletableDeferred<Unit>() | ||
private val intentsFlow = MutableSharedFlow<INTENT>() | ||
|
||
override fun getIntents( | ||
mapOperation: (INTENT) -> Flow<PARTIAL_UI_STATE>, | ||
): Flow<PARTIAL_UI_STATE> = intentsFlow | ||
.onSubscription { intentsFlowListenerStarted.complete(Unit) } | ||
.flatMapConcurrently( | ||
transform = mapOperation, | ||
) | ||
|
||
override suspend fun setIntent(intent: INTENT) { | ||
intentsFlowListenerStarted.await() | ||
intentsFlow.emit(intent) | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
core/src/main/java/eu/krzdabrowski/starter/core/presentation/mvi/InternalChangesDelegate.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface InternalChangesDelegate<PARTIAL_UI_STATE> { | ||
fun getInternalChanges(): Flow<PARTIAL_UI_STATE> | ||
|
||
suspend fun setInternalChanges(vararg internalChangesFlows: Flow<PARTIAL_UI_STATE>) | ||
} |
27 changes: 27 additions & 0 deletions
27
...rc/main/java/eu/krzdabrowski/starter/core/presentation/mvi/InternalChangesDelegateImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package eu.krzdabrowski.starter.core.presentation.mvi | ||
|
||
import eu.krzdabrowski.starter.core.coroutines.flatMapConcurrently | ||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.MutableSharedFlow | ||
import kotlinx.coroutines.flow.asFlow | ||
import kotlinx.coroutines.flow.emitAll | ||
import kotlinx.coroutines.flow.onSubscription | ||
|
||
class InternalChangesDelegateImpl<PARTIAL_UI_STATE> : InternalChangesDelegate<PARTIAL_UI_STATE> { | ||
|
||
private val internalChangesPartialStateFlowListenerStarted = CompletableDeferred<Unit>() | ||
private val internalChangesPartialStateFlow = MutableSharedFlow<PARTIAL_UI_STATE>() | ||
|
||
override fun getInternalChanges(): Flow<PARTIAL_UI_STATE> = | ||
internalChangesPartialStateFlow | ||
.onSubscription { internalChangesPartialStateFlowListenerStarted.complete(Unit) } | ||
|
||
override suspend fun setInternalChanges(vararg internalChangesFlows: Flow<PARTIAL_UI_STATE>) { | ||
internalChangesPartialStateFlowListenerStarted.await() | ||
internalChangesPartialStateFlow.emitAll( | ||
// to flatten Flow with queue behaviour like in userIntents() Flow but without ::mapIntents | ||
internalChangesFlows.asFlow().flatMapConcurrently { it }, | ||
) | ||
} | ||
} |