Android: ViewModel to View communication using LiveData and Sealed Classes in MVVM

Praveen VK
2 min readOct 30, 2021

--

The recommended and now widely followed architecture in Android nowadays in Model View ViewModel or MVVM for short. The image below shows an architecture diagram :

MVVM data flow

The flow would generally be: user lands on a page i.e., view (activity/ fragment). The view would be observing an object in its associated viewModel to render content on screen. The ViewModel would request the data from repository. The repository would then fetch the data from one of the sources that could be a database or remote data source and return it to ViewModel.

Passing data from ViewModel to View

We should not be passing the View (Activity/ Fragment) context to ViewModel hence a method in View can’t be called by ViewModel to pass the data it contains. MVVM aims to segregate the data from view as the view may be destroyed in certain scenarios eg device rotation. This is where live data comes into play. In the view, we can set observers to LiveData of ViewModel

viewModel.pageLiveData.observe(viewLifecycleOwner, Observer { data->
handleData(data)
})

The benefit of using LiveData is it is lifecycle aware unlike callbacks from interface listeners. We would have to check if the page is still active when we receive a callback from the interface, but when using LiveData we would receive data in observer only if the view is active. In case if the user had put the app in the background and there was a change in the data being observed, the view is notified about the updated data once the app comes to the foreground and the view is active. Therefore we don’t have to worry about checking whether the view is active when handling the data received from LiveData.

ViewModel would be wrapping the data to be returned in LiveData so that view can observe it eg

class MainViewModel(application: Application) : AndroidViewModel(application) {    private val _pageLiveData: MutableLiveData<PageState> = MutableLiveData<PageState>()
val pageLiveData: LiveData<PageState> = _pageLiveData
fun loadPageData() {
CoroutineScope(Dispatchers.IO).launch {
try {
_pageLiveData.postValue(PageState.Loading())
val response = RetrofitBuilder.apiService.getCountryData()
_pageLiveData.postValue(PageState.Success(response))
} catch (exception: Exception) { _pageLiveData.postValue(PageState.Failure(exception.message))
}
}
}sealed class PageState {
object Loading: PageState()
object Success: PageState(val content: Content)
data class Failure(val message: String): PageState()
}
}

Sealed Class to change state of view

The view should not have business logic and the change of state to loading ie showing a progress bar and updating the content once received should be controlled by ViewModel. Therefore using sealed class ViewModel can inform View the state to be rendered for users. The observer in View would then handle different page states eg

fun handleData(pageState: PageState) {
when(pageState) {
is PageState.Failure -> //Show failure msg
PageState.Loading -> //Show loader
PageState.Success -> //set data to view
}
}

--

--