Stylianos Gakis
12/13/2021, 8:56 AMmap
on the originalStateFlow but realized I wouldn’t be able to start a new flow inside it and emit those values whenever they come in, map needed a value directly or at least from a suspending function. I found out transformLatest
exists and it was the only thing that I managed to do to get it compiling properly.
val viewState = originalStateFlow.transformLatest { uuidResult: Result<UUID> ->
when(uuidResult) {
Error -> emit(ViewState.Error)
Success -> {
val uuid = uuidResult.value
startSomeOtherSubscription(uuid).collect { subscriptionResult -> // [1] See below for a question
when(subscriptionResult) {
Error -> emit(ViewState.Content(uuid = uuid, extraData = null))
Success -> {
val extraData = extraDataSupendingApiCall(subscriptionResult.value)
emit(ViewState.Content(uuid = uuid, extraData = extraData))
}
}
}
}
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = ViewState.Loading,
)
Now I wonder:
1. This collect
inside the flow transformation feels a bit awkward no? Will it work just fine since we’re doing transformLatest
anyway, meaning that it would be cancelled in case we get a new uuidResult? If I just did transform
without latest
would it keep this flow alive forever (or maybe until viewModelScope is cancelled since it’s launched there with StateIn
) and be a problem?
2. Is doing such a whole new collect flow inside this transform context something that is safe to do?
3. Is there a better way to model this entirely? It feels a bit awkward the way that I’ve done it, and I am not sure it’s because I am not used to this, the requirement of what I am doing is just weird itself or if I am just plain out doing something wrong/suboptimal.Nick Allen
12/13/2021, 8:38 PMstartSomeOtherSubscription(uuid).collect { ... }
will block new UUIDs from being processed.
2. Yes, it is safe. It's a normal part of creating operators that combine other flows.
3. It's awkward because you have a lot of nesting. Easiest way to fix that is the break parts out into separate helper methods. Here's an example where I replaced the collect
with map
and then emitAll
the result. It's not any safer or more optimal, but sometimes emitAll
can read better, IMO.
val viewState = originalStateFlow.transformLatest { uuidResult: Result<UUID> ->
when(uuidResult) {
Error -> emit(ViewState.Error)
Success -> emitAll(startSomeOtherSubscriptionHelper(uuidResult.value))
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = ViewState.Loading,
)
private fun startSomeOtherSubscriptionHelper(uuid: UUID) = startSomeOtherSubscription(uuid)
.map { subscriptionResult ->
when(subscriptionResult) {
Error -> ViewState.Content(uuid = uuid, extraData = null)
Success -> ViewState.Content(
uuid = uuid,
extraData = extraDataSupendingApiCall(subscriptionResult.value)
)
}
}
}
(just typed out in slack so please forgive any errors but I think the intent is clear)Stylianos Gakis
12/16/2021, 7:44 AM