Question about the MVVM pattern, Chapter 12

Dear Friends,

please excuse my not very good English, I hope you understand my question. I’m having a little trouble understanding how the View (Fragment or Activity) should really interact with the ViewModel. For example, in the Chapter 12 of the book, page 162, there is a sample where the view gets a list from the ViewModel to show it on the UI.

showLoading()
viewModel.getSavedMovies().observe(this,Observer{movies->
hideLoading()
//do something with the movies
})

This example leads me to some questions:

  • It has an advantage. The getSavedMovies function from the ViewModel returns something, and because of that, it is unit-testable, but

If the view is deciding what happens when the user clicks on a button or the app is started, are we not putting on the view business logic that should be on the ViewModel?

And if the function getMovies is a suspend function, then we have to include in the view the code for the coroutine and so on, which maybe should be better placed in the ViewModel and use the viewmodelscope to auto-cancel the coroutine when the ViewModel is disposed.

Should not the view simply forward that an event has happened on the view, and let the ViewModel decide what should happen. For example (the following is pseudocode):

fun onCreate(){
//Create the viewmodel and so on.
viewModel.movies.observe---> paint movies.
}

fun onButtonClicked(){
viewModel.onButtonClicked();
}

And on the viewmodelSide:

class Viewmodel{

val movies:MutableLiveData

fun onButtonClicked(){
//Maybe an async process with the help from the viewmodelscope and coroutines.
movies.postValue(repository.GetMovies());
}
}

In my sample, the view does not know what happens when the view is created or a user requests a refresh by clicking a button. The view forwards the event to the ViewModel, and the ViewModel updates an observable object. The ViewModel does not know what happens on the view when this observable object is updated.

Of course, this approach has a problem. If our view only forwards events to the ViewModel, and only reacts to changes on the observables, then:

  • The complexity of the code increases. For example in the use case that we want to save something, and it has to be validated before saving it, then:
  • The view sends the event with the data to the ViewModel.

  • The ViewModel validates it and notifies the error, the progress state, o the success state through observables to the view (this increases complexity because now we have a lot of observables for a simple task).

  • The function in the ViewModel that receives the data to be stored is not a function that returns something, and can no be unit-tested.

  • Of course you could have an internal function on the ViewModel that validates the input before saving, that returns true or false to allow a unit test, but then you have to (correct me if I’m wrong) to make it public to be testable, although this function is only used inside the ViewModel and should be private…

On the other side, the whole logic resides on the ViewModel, and the view is agnostic, it does not know what happens when a button is pressed or an event happens.

Maybe I’ m not understanding well how we should work with view models.

May you give me some advice?

Thanks in advance!

@aldominium Can you please help with this when you get a chance? Thank you - much appreciated! :]