Advanced ViewModels (part II): Data Holder.

Lucas Nobile
3 min readSep 15, 2020

--

ViewModel as the bridge between the View and the Model.

TL;DR: We can pass parameters to our ViewModel, use it as a data holder, also to share data between Fragments, and to persist its state across process recreation.

Thanks to LiveData, our ViewModel becomes a data holder 🤓

Within our ViewModel, we will have the LiveData variables as usual:

private val _news = MutableLiveData<Resource<ArrayList<News>>>()
val news: LiveData<Resource<ArrayList<News>>> = _news

Now, we will have a method that exposes the actual news:

fun getNews(): ArrayList<News>? = _news.value?.data

⚠️ You may be wondering why exposing the LiveData value and not observing it? That’s a good question and also is good to remember the GRASP patterns or principles. If you are familiar with the High Cohesion and Low Coupling principles then you know about GRASP. At this point, I would like to recall, specifically, the Information expert principle whose main goal is to determine what is the information we will store in an object and what to do with that information. In our case, the information is the data that will live in the LiveData variables (no pun intended) and how we can access that data: by observing it and getting notified when its value has changed or by value directly.

As always, it depends on the use case. We will be covering a common use case for observing LiveData and other one to access its value directly.

So, in our View (Fragment/Activity) we will have the following to show data, if there is something to show, otherwise, we will be performing a request to get fresh data:

override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
setupNewsObserver()
getNews()
}
private fun setupNewsObserver() {
// Observe the LiveData
viewModel.news.observe(viewLifecycleOwner, Observer { resource ->
when (resource) {
is Resource.Loading -> {
binding.pbLoading.show() }
is Resource.Success -> {
binding.pbLoading.hide()
resource.data?.let { news ->
if (news.isNotEmpty()) {
showNews(news)
} else {
showNoResults()
}
}
}
is Resource.Error -> {
binding.pbLoading.hide()
showError(resource.message)
}
}
})
}
private fun getNews() {
// Access the LiveData value
viewModel.getNews()?.let { news ->
showNews(news)
} ?: run { viewModel.searchNews(<query>) }
}

Finally, the pattern here is:

viewModel.getData()?.let { data ->
showData(data)
} ?: run { viewModel.requestData() }

We’re leveraging that our ViewModel survives configuration changes and if there is some data in our LiveData variable we can show it right away 💪

Let me share with you all the repo:

And my tech talk about Advanced ViewModels on Android for GDG Córdoba, Argentina (in Spanish):

Please, continue reading the next posts in this multi-part series for more tips regarding advanced ViewModels 🙌

--

--