My application usually repeats a specific pattern on its screens:
- may get some data from some API to build the initial state
- handle some user actions
- navigate accordingly
For many screens, using the stateIn
operator to transform my cold flows into hot flows works like a charm. It's also pretty easy to test my VMs when I follow this pattern:
val state: StateFlow<UiState> = combine(
initializationUseCase(),
someValueChangedFromUi,
) { initializationResult, selectedValue ->
when (initializationResult) {
Result.Loading -> initialUiState.copy(isLoading = true)
is Result.Error -> initialUiState.copy(
isLoading = false,
error = initializationResult.error,
)
is Result.Success -> initialUiState.copy(
isLoading = false,
someList = initializationResult.data,
selectedValue = selectedValue,
)
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = initialUiState,
)
I struggle when I have a cold flow that cannot be turned into a StateFlow - because this flow may produce repeated data, and I need to process this repeated data. Also, if the user sends the app to the background and opens it again, I shouldn't receive the last value.
I tried to collect this flow on my init block:
private val result = MutableStateFlow("")
init {
viewModelScope.launch {
repository.myDataStream.collect {
result.update { "the result" }
}
}
}
val state = combine(
repository.isSyncing(),
result,
) { isSyncing, result ->
when {
isSyncing -> {
UiState(isLoading = true)
}
else -> {
UiState(myResult = result)
}
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = UiState(),
)
But this created two new issues:
- I'll collect
myDataStream
even if the user leaves my app. - I'll store the result in a
MutableStateFlow
; since it's conflated, repeated results will not trigger combine. I considered setting it to a null value after processing a myDataStream value, but it seems a bit of a hack and error-prone.
I headed this amazing article: https://bladecoder.medium.com/smarter-shared-kotlin-flows-d6b75fc66754, but I'm still struggling to figure out how to solve this.