Skip to content

Instantly share code, notes, and snippets.

@ZakTaccardi
Last active May 22, 2020 16:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZakTaccardi/33008792e062a1bfcedba0703b4ebe06 to your computer and use it in GitHub Desktop.
Save ZakTaccardi/33008792e062a1bfcedba0703b4ebe06 to your computer and use it in GitHub Desktop.
Simple MVI coroutine example
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