ViewModel Integration

Learn how to add a ViewModel and an adapter class to our projects.

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.

Press + to interact
package com.example.globalnewsapp.ui.viewmodel
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.globalnewsapp.models.Article
import com.example.globalnewsapp.models.NewsResponse
import com.example.globalnewsapp.repository.NewsRepository
import com.example.globalnewsapp.util.Resource
import kotlinx.coroutines.launch
import retrofit2.Response
class NewsViewModel(val newsRepository: NewsRepository) : ViewModel() {
val breakingNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var breakingNewsPage = 1
val searchNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var searchNewsPage = 1
init {
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 ...