Last active
May 22, 2020 16:12
-
-
Save ZakTaccardi/33008792e062a1bfcedba0703b4ebe06 to your computer and use it in GitHub Desktop.
Simple MVI coroutine example
This file contains hidden or 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
interface MviViewModel<in I, out VS, out SE> { | |
val states: Flow<VS> | |
val sideEffects: Flow<SE> | |
fun send(intention: I) | |
} | |
class SimpleViewModel(scope: CoroutineScope) : MviViewModel<Intention, State, SideEffect> { | |
private val _states = MutableStateFlow(0) // this is an always have an initial state example | |
private val _sideEffects = Channel<SideEffect>(capacity = Channel.UNLIMITED) // we don't want the actor coroutine to ever suspend, in case there is no UI consuming | |
override val states: Flow<Int> = _states | |
override val sideEffects: Flow<SideEffect> = _sideEffects.receiveAsFlow() | |
// actor is an ongoing coroutine, so there can be no race conditions for state confined to it | |
// it processes incoming intentions one by one to build a new state | |
// means you can launch this actor with the default dispatcher to maximize cpu core usage | |
private val actor = scope.actor<Intention>( | |
capacity = Channel.UNLIMITED // unlimited capacity is needed so `actor.offer(..)` never returns `false` | |
) { | |
// `StateFlow` requires an initial state - but you could use the upcoming `.shareIn(..)` operator to not require an initial state | |
// also, any additional state you share across processing `Intention`s can be stored here, though a single state object is :thumbs up | |
var currentState: State = _states.value | |
for (intention: Intention in channel) { // every intention sent through `actor.offer` emits here | |
// simple example, but you could add logging for every intention sent through here, as well as every state change | |
currentState = when (intention) { | |
Intention.Increment -> currentState + 1 | |
Intention.Decrement -> currentState - 1 | |
} | |
// update `Flow<State>` | |
_states.value = currentState | |
} | |
} | |
override fun send(intention: Intention) { | |
actor.offer(intention) // not in a suspending context, so no `.send(..)` available | |
} | |
} | |
typealias State = Int | |
typealias SideEffect = Unit | |
sealed class Intention { | |
object Increment : Intention() | |
object Decrement : Intention() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment