Lukasz Kalnik
09/08/2022, 12:43 PMEither
with other wrappers? I have a MutableStateFlow<Either<DataMissing, T>>
and want to update it only if it's a `Right`:
val automations: MutableStateFlow<Either<DataMissing, AutomationList>>
private fun handleEvent(event: EntityModel) {
if (event is AutomationUpdatePendingStateEvent) {
automations.update { automationsEither ->
automationsEither.map { automations ->
automations.updateAutomationState(
automationId = event.automationId,
pendingState = event.deviceState,
provisioningComplete = event.provisioningComplete
)
}
}
}
}
Lukasz Kalnik
09/08/2022, 12:45 PMfun <T> MutableStateFlow<Either<DataMissing, T>>.updateIfRight(function: (T) -> T)
simon.vergauwen
09/08/2022, 12:49 PMinline fun <E, A> MutableStateFlow<Either<E, A>>.update(f: (A) -> A) =
update {
when(it) {
is Either.Right -> f(it.value).right()
is Either.Left -> it
}
}
simon.vergauwen
09/08/2022, 12:51 PMval automations: MutableStateFlow<Either<DataMissing, AutomationList>>
private fun handleEvent(event: EntityModel) {
automations.update { list ->
if (event !is AutomationUpdatePendingStateEvent) list
else automations.updateAutomationState(
automationId = event.automationId,
pendingState = event.deviceState,
provisioningComplete = event.provisioningComplete
)
}
}
Lukasz Kalnik
09/08/2022, 12:54 PMMutableStateFlow
could be treated like a monadLukasz Kalnik
09/08/2022, 12:54 PMsimon.vergauwen
09/08/2022, 12:59 PMMutableStateFlow
wraps an atomic value, so it doesn't form a monad. It also doesn't have map
or flatMap
either IIRC.Lukasz Kalnik
09/08/2022, 1:05 PMfun <T> MutableStateFlow<Either<DataMissing, T>>.updateIfRight(function: (T) -> T) = update { either ->
either.map { function(it) }
}
simon.vergauwen
09/08/2022, 1:05 PMinline
there as well ๐simon.vergauwen
09/08/2022, 1:06 PMLukasz Kalnik
09/08/2022, 1:06 PMstojan
09/08/2022, 1:33 PMRight
then why do you need the Either
?Lukasz Kalnik
09/08/2022, 1:40 PMstojan
09/08/2022, 1:46 PMMutableStateFlow
in the ViewModel to expose the state to the UI.... the state is typically a combination of sealed and/or data classes....
and errors from other layers typically map to some state (the state might display an error, but it is a valid state as far as the UI is concerned)Lukasz Kalnik
09/08/2022, 1:58 PMRepository
(kind of domain layer) which makes the calls to backend, listens to update events and exposes the different partial states of the system to the ViewModels.Lukasz Kalnik
09/08/2022, 1:58 PMLukasz Kalnik
09/08/2022, 2:00 PMLukasz Kalnik
09/08/2022, 2:02 PMstojan
09/08/2022, 2:04 PMour backend is generally very stable, so there are almost never errorson mobile most common errors would be connectivity issue ๐
Lukasz Kalnik
09/08/2022, 2:04 PMLukasz Kalnik
09/08/2022, 2:05 PMLukasz Kalnik
09/08/2022, 2:06 PMstojan
09/08/2022, 2:07 PM// internally backed by StateFlow, but that is an implementation detail
fun state(): Flow<Thing>
suspend fun updateThing(): Either<Error, Unit>
new screen would subscribe to state()
which never errors. If they need fresh data, they would call updateThing()
, if this works, the state()
flow would emit the latest value, if this errors, the screen can handle the error
error is never part of the state()
flow, so if other screens observe that flow, it's never clearedstojan
09/08/2022, 2:08 PMLukasz Kalnik
09/08/2022, 2:09 PMupdateThing()
? does it return Either<Error, Unit>
?stojan
09/08/2022, 2:09 PMLukasz Kalnik
09/08/2022, 2:10 PMstate()
before you first initialize it?Lukasz Kalnik
09/08/2022, 2:10 PMLukasz Kalnik
09/08/2022, 2:10 PMMutableStateFlow()
on construction.Lukasz Kalnik
09/08/2022, 2:10 PMLukasz Kalnik
09/08/2022, 2:11 PMStateFlow<Thing?>
stojan
09/08/2022, 2:11 PMprivate val state: MutableStateFlow<Thing?>
fun state(): Flow<Thing> = state.filterNotNull()
that way it suspends until it's set to the first item, and also supports resettingLukasz Kalnik
09/08/2022, 2:11 PMLukasz Kalnik
09/08/2022, 2:12 PMLukasz Kalnik
09/08/2022, 2:12 PMLukasz Kalnik
09/08/2022, 2:12 PMLukasz Kalnik
09/08/2022, 2:12 PMNotInitialized
state for thisLukasz Kalnik
09/08/2022, 2:13 PMstojan
09/08/2022, 2:14 PMSo you can show a loading indicator as long as the flow is suspended (on the first access)yes...
state.onStart { emit(Loading) }....
Lukasz Kalnik
09/08/2022, 2:14 PMstojan
09/08/2022, 2:14 PMstate.stateIn(viewModelScope, ..., default = Loading)
Lukasz Kalnik
09/08/2022, 2:14 PMstojan
09/08/2022, 2:16 PMLukasz Kalnik
09/08/2022, 2:16 PMLukasz Kalnik
09/08/2022, 2:17 PM