ViewModel Integration
Learn how to add a ViewModel and an adapter class to our projects.
We'll cover the following...
NewsViewModel
Let’s add the ViewModel to the NewsApplication
project.
Now, let’s create a package and name it ViewModel
, and inside it create the NewsViewModel
and NewsViewModelProvider
classes.
package com.example.globalnewsapp.ui.viewmodelimport android.app.Applicationimport androidx.lifecycle.MutableLiveDataimport androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport com.example.globalnewsapp.models.Articleimport com.example.globalnewsapp.models.NewsResponseimport com.example.globalnewsapp.repository.NewsRepositoryimport com.example.globalnewsapp.util.Resourceimport kotlinx.coroutines.launchimport retrofit2.Responseclass NewsViewModel(val newsRepository: NewsRepository) : ViewModel() {val breakingNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()var breakingNewsPage = 1val searchNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()var searchNewsPage = 1init {getBreakingNews("us")}fun getBreakingNews(countryCode: String) = viewModelScope.launch {breakingNews.postValue(Resource.Loading())val response = newsRepository.getBreakingNews(countryCode, breakingNewsPage)breakingNews.postValue(handleBreakingNewsResponse(response))}fun searchNews(searchQuery: String) = viewModelScope.launch {searchNews.postValue(Resource.Loading())val response = newsRepository.searchNews(searchQuery, searchNewsPage)searchNews.postValue(handleSearchNewsResponse(response))}private fun handleBreakingNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {if (response.isSuccessful) {response.body()?.let { resultResponse ->return Resource.Success(resultResponse)}}return Resource.Error(response.message())}private fun handleSearchNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {if (response.isSuccessful) {response.body()?.let { resultResponse ->return Resource.Success(resultResponse)}}return Resource.Error(response.message())}fun saveArticle(article: Article) = viewModelScope.launch {newsRepository.insertArticle(article)}fun fetchSavedArticles() = newsRepository.getSavedNews()fun deleteArticle(article: Article) = viewModelScope.launch {newsRepository.deleteArticle(article)}}
Let’s take a closer look at the code above. We have the getBreakingNews
method, which takes countryCode
as its argument and launches a coroutine because it’s a suspend function.
fun getBreakingNews(countryCode: String) = viewModelScope.launch {
breakingNews.postValue(Resource.Loading())
val response = newsRepository.getBreakingNews(countryCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
The above code calls the getBreakingNews
suspend function from the repository, which takes countryCode
and breakingNewsPage
as arguments and launches them in a coroutine to be observed by the fragment. The same case applies to the searchNews
method.
We have response handlers that return either a success or an error message whenever a network operation is performed. The handleBreakingNewsResponse
function takes the NewsResponse
as an argument. If the response is successful, the body of our response and the data are returned as shown below.
private fun handleBreakingNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
NewsViewModelProvider
We create a custom ViewModelProvider.Factory
because our program can’t create it by default. The ...