Retrofit with ViewModel in Kotlin (Part-2)

Krishna sundar
Developer Community SASTRA
3 min readMar 7, 2022

I strongly recommend you to read the Part-1 of this article.

Part 1 : https://medium.com/@krish.apk/f9e705e77144

Implementing ViewModel

ViewModel

Steps:

  1. Setting up a Repository class.
  2. Creating a ViewModelProviderFactory class.
  3. Creating a ViewModel class by combining the Repository class and the ViewModelProviderFactory class.
  4. Merging the ViewModel class into Views and calling the API.

Step 1: Repository class

Repository class is created to enable an application to consume data without worrying about its origin.

MoviesRepository.kt:

import com.example.sitmyseat.api.RetroFitInstance

class MoviesRepository() {
suspend fun getMoviesR() = RetroFitInstance.api.getMovies()
}

→ An API call is created in the above block.

→The suspend function is used to make it run in co-routines.

Step 2: ViewModelProviderFactory class

ViewModelProvider.Factory instantiates ViewModel objects, with or without constructor parameters.

MoviesViewModelProviderFactory.kt :

package com.example.sitmyseat.ui

import MoviesRepository
import MoviesViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class MoviesViewModelProviderFactory(val moviesRepository: MoviesRepository) : ViewModelProvider.Factory {

override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MoviesViewModel(moviesRepository) as T
}
}

→ Here, we can pass the argument named moviesRepository, whereas the argument cannot be directly passed through the main ViewModel class.

Step 3: ViewModel class

We are moving onto the major part of our ViewModel implementation.

MoviesViewModel.kt :

package com.example.sitmyseat.ui

import MoviesRepository
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.sitmyseat.models.MovieResponse
import com.example.sitmyseat.utils.Resource
import kotlinx.coroutines.launch
import retrofit2.Response

class MoviesViewModel(val moviesRep: MoviesRepository) : ViewModel() {

val moviesPage: MutableLiveData<Resource<MovieResponse>> = MutableLiveData()

init {
getMoviesVM()
}

private fun getMoviesVM() = viewModelScope.launch {
moviesPage.postValue(Resource.Loading())
val response = moviesRep.getMoviesR()
moviesPage.postValue(handleResponse(response))
}

private fun handleResponse(response: Response<MovieResponse>) : Resource<MovieResponse> {
if(response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
}

Explanation:

class MoviesViewModel(val moviesRep: MoviesRepository)

→As we cannot directly pass the argument “moviesRep”, we have to create the ProviderFactory class. (converted later in step 4).

val moviesPage: MutableLiveData<Resource<MovieResponse>> = MutableLiveData()

liveData acts as a notify-changer whenever the dataset changes.

moviesPage.postValue(Resource.Loading())

→ A Loading class is added to the MutableLiveData. (handled in step 4)

val response = moviesRep.getMoviesR()
moviesPage.postValue(handleResponse(response))

→ The above code will add the response of the API call to the MutableLiveData.

Step 4: Merging

MainActivity.kt :

package com.example.sitmyseat.ui

import MoviesRepository
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.sitmyseat.R
import com.example.sitmyseat.adapters.MoviesAdapter
import com.example.sitmyseat.utils.Resource
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
lateinit var viewModel: MoviesViewModel
lateinit var moviesAdapter : MoviesAdapter
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val repository = MoviesRepository()
val provider = MoviesViewModelProviderFactory(repository)
viewModel = ViewModelProvider(this, provider).get(MoviesViewModel::class.java)

setRecyclerView(this)

viewModel.moviesPage.observe(this, { response ->
when(response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { newsResponse ->
moviesAdapter.differ.submitList(newsResponse.items)
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An error occured: $message")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})


}

private fun hideProgressBar() {
paginationProgressBar.visibility = View.INVISIBLE
}

private fun showProgressBar() {
paginationProgressBar.visibility = View.VISIBLE
}

fun setRecyclerView(context: Context){
moviesAdapter = MoviesAdapter()
rvMovies.apply {
adapter=moviesAdapter
layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
}
}
}

Explanation:

viewModel=
ViewModelProvider(this,provider).get(MoviesViewModel::class.java)

→ This line converts our providerFactory class into ViewModel by taking the Repository class as an argument.

The Resource file will be unwrapped in the following code:

when(response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { newsResponse ->
moviesAdapter.differ.submitList(newsResponse.items)
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An error occured: $message")
}
}
is Resource.Loading -> {
showProgressBar()
}
}

Bonus tip:

Project structure

Keeping your files and packages well structured will help a lot in terms of the MVVM Architecture.

Conclusion:

Yay, you have completed the article!

Hope you have learnt something. Feel free to drop your thoughts and feedback.

Source code: https://github.com/krish-dev-7/sitMySeat

--

--