Lukasz Kalnik
06/12/2024, 8:41 AMsealed class MyScreenState
data object EmptyState : MyScreenState()
data class ContentState(val title: String, val content: String) : MyScreenState()
How do you handle conditional uiState
updates based on the current subclass?
val uiState: MutableStateFlow<MyScreenState> = MutableStateFlow(EmptyState)
fun onContentChanged(newContent: String) {
uiState.update { (it as? ContentState)?.copy(content = newContent) ?: it }
}
This seems neither elegant nor readable.Lukasz Kalnik
06/12/2024, 8:43 AMwhen
, but it still requires the lone it
inside of update()
uiState.update {
when (it) {
is ContentState -> it.copy(content = newContent)
else -> it
}
}
Lukasz Kalnik
06/12/2024, 8:46 AMupdate
block in an if
or ?.let
is not thread safe. There could be a race condition, where the uiState
gets updated by another thread just after executing the if
or ?.let
and we overwrite this other update.
(uiState.value as? ContentState)?.let { currentUiState ->
// potential race condition and overwriting parallel updates
uiState.value = currentUiState.copy(content = newContent)
}
ephemient
06/12/2024, 9:08 AMrun {
uiState.update {
if (it !is ContentState) return@run
it.copy(content = newContent)
}
}
which could be slightly more efficient since it avoids a CAS in the !is
caseLukasz Kalnik
06/12/2024, 9:12 AMLukasz Kalnik
06/12/2024, 9:14 AMpublic inline fun <T, reified U : T> MutableStateFlow<T>.updateIfSubtype(function: (U) -> T) {
while (true) {
val prevValue = value
if (prevValue is U) {
val nextValue = function(prevValue)
if (compareAndSet(prevValue, nextValue)) {
return
}
} else return
}
}
Usage:
uiState.updateIfSubtype<MyScreenState, ContentState> { it.copy(content = newContent) }
The only disadvantage is that you have to explicitly specify the parent type.ephemient
06/12/2024, 9:15 AMuiState.updateIfSubtype<_, ContentState> { ... }
or swap the parameters around with
inline fun <reified U, T> MutableStateFlow<T>.updateIfSubtype(function: (U) -> T) where U : T {
to write uiState.updateIfSubtype<ContentState, _> { ... }
Lukasz Kalnik
06/12/2024, 9:16 AMLukasz Kalnik
06/12/2024, 9:16 AMMichael Krussel
06/12/2024, 11:26 AMMyScreenState
and let each state return an updated state based on its own logic. If there is no change it just returns itself.Lukasz Kalnik
06/12/2024, 11:47 AM