https://kotlinlang.org logo
#android-architecture
Title
# android-architecture
v

Vincent Williams

08/06/2020, 2:43 AM
when using sealed classes for the viewstate in android, how do you handle updating part of the viewstate? For example:
Copy code
sealed class ViewState {
    object Idle : ViewState()
    data class Content(
        val headerText: String,
        val bodyText: String
    ) : ViewState()
    data class Error(
        val message: String
    ) : ViewState
}
How do I update just the title for example? I see a lot of examples use sealed classes but they dont seem so great in practice...
s

streetsofboston

08/06/2020, 2:50 AM
Write diffing code, code that can detect the differences between the current and new state and only updates the changes/diffs. This is not trivial and that is why folks often have more than one (partial) view-states being emitted by a view model, that then won't need this diffing code.
v

Vincent Williams

08/06/2020, 2:51 AM
ya my previous approach was to have a separate stream for loading and error messages, but that seems not to be the approach most people take
o

OG

08/06/2020, 4:32 AM
There's an extension for LiveData called distinctUntilChanged() that you can use. This way, if you have a LiveData holding the viewstate and you update the LiveData in your VM (for example), then it will only emit the new viewState to your View layer if there was an actual change in the previous state to the new view state. This way you don't have to handle the diffing logic yourself
v

Vincent Williams

08/06/2020, 4:54 AM
I'm not sure this answers the question. I'm not using livedata I'm using StateFlow. It already does the behavior you described but that doesn't answer the initial question
j

Joost Klitsie

08/06/2020, 5:24 AM
@Vincent Williams I think sealed classes are great, but using them for view states is a bit weird to me (and if anyone has a link explaining why they could be great, I'd love to read it). Basically you are mapping domain objects into partial view objects, only to map that within the View itself with extra logic (aka harder to test as it is in the view layer) to actual view properties. So that said, to get to your question: Perhaps you can use multiple view states. Like one for the mutable content (which can be Idle, Success, Error, perhaps Loading) and one for fixed content (like title). In that case you separately observe them and do a thing like
otherViewState.value = otherViewState.value.copy(title = newTitle)
Or you could give the original ViewState some abstract fields like title which all the other child have to implement:
Copy code
sealed class ViewState {
	abstract val title: String
	data class Idle(override val title: String) : ViewState()
	data class Content(
		override val title: String,
		val bodyText: String,
		val headerText: String
	) : ViewState()
	data class Error(
		override val title: String,
		val message: String
	) : ViewState()
}
Which you can update as such:
Copy code
viewState.value = when (val oldValue = viewState.value) {
		is ViewState.Idle -> oldValue.copy(title = newTitle)
		is ViewState.Content -> oldValue.copy(title = newTitle)
		is ViewState.Error -> oldValue.copy(title = newTitle)
	}
Or, what I would do, define 1 view state data class for your view that contains everything:
Copy code
data class ViewState(
	val title: String,
	val bodyText: String,
	val headerText: String,
	val errorMessage: String?,
	val isErrorVisible: Boolean
)
which you simply update your stateflow by
viewState.value = viewState.value.copy(title = newTitle)
a

Adrian Blanco

08/06/2020, 5:40 AM
Basically I just have the state as a bunch of BehaviorSubject or MutableStateFlow, and then combine them to construct view states. Any manual changes in the view state come from emissions in upstream subjects, then sealed classes work really well
g

George Theocharis

08/06/2020, 7:24 AM
State as sealed classes is only good for views that have distinct states. For example a screen that shows either loading, or the results, or an error. When you want to model state that is not distinct try having one state class with stateful properties. This is a great article https://zsmb.co/thoughts-about-state-handling-on-android/
👍 1
v

Vincent Williams

08/06/2020, 5:25 PM
@George Theocharis Thanks for that article! That makes sense that some views would not take so well to sealed classes. Especially because error handling does not always result in an error "state". Sometimes its just something like a snackbar popup
g

George Theocharis

08/06/2020, 5:27 PM
Yes. Sometimes i model my state properties with sealed classes. As for navigation, it’s probably better to have a secondary stream like flow to send one time events also called effects.
v

Vincent Williams

08/06/2020, 5:27 PM
for navigation, I have just been passing the navcontroller into the viewmodel constructor directly
or at least that was my plan although that may cause a memory leak so im not sure its the best idea
130 Views