Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing same MutableLiveData between Repository and ViewModel

I'm in the process of wrapping my head around Architecture Components / MVVM.

Let's say I have a repository, a ViewModel and a Fragment. I'm using a Resource class as a wrapper to expose network status, like suggested in the Guide to architecture components.

My repository currently looks something like this (simplified for brevity):

class MyRepository {

  fun getLists(organizationId: String) {
    var data = MutableLiveData<Resource<List<Something>>>()
    data.value = Resource.loading()

    ApolloClient().query(query)
        .enqueue(object : ApolloCall.Callback<Data>() {
            override fun onResponse(response: Response<Data>) {
                response.data()?.let {
                    data.postValue(Resource.success(it))
                }
            }

            override fun onFailure(exception: ApolloException) {
                data.postValue(Resource.exception(exception))
            }
        })
}

Then in the ViewModel, I also declare a MutableLiveData:

var myLiveData = MutableLiveData<Resource<List<Something>>>()

fun getLists(organizationId: String, forceRefresh: Boolean = false) {
   myLiveData = myRepository.getLists(organizationId)
}

Finally, the Fragment:

viewModel.getLists.observe(this, Observer {
        it?.let {
            if (it.status.isLoading()) showLoading() else hideLoading()

            if (it.status == Status.SUCCESS) {
                it.data?.let {
                    adapter.replaceData(it)
                    setupViews()
                }
            }

            if (it.status == Status.ERROR) {
                // Show error
            }
        }
    })

As you see, there will be an issue with the observer not being triggered, since the LiveData variable will be reset in the process (the Repository creates a new instance).

I'm trying to figure out the best way to make sure that the same LiveData variable is used between the Repository and ViewModel.

I thought about passing the LiveData from the ViewModel to the getLists method, so that the Repository would be using the object from the ViewModel, but even if it works, it seems wrong to do that.

What I mean is something like that:

ViewModel

var myLiveData = MutableLiveData<Resource<List<Something>>>()

fun getLists(organizationId: String, forceRefresh: Boolean = false) {
   myRepository.getLists(myLiveData, organizationId)
}

Repository

fun getLists(data: MutableLiveData<Resource<List<Something>>>, organizationId: String) {
    ...
}
like image 780
Mathbl Avatar asked Jun 18 '18 13:06

Mathbl


1 Answers

I think I figured out how to do it, thanks to @NSimon for the cue.

My repository stayed the same, and my ViewModel looks like this:

class MyViewModel : ViewModel() {
  private val myRepository = MyRepository()

  private val organizationIdLiveData = MutableLiveData<String>()
  private val lists = Transformations.switchMap(organizationIdLiveData) { organizationId -> myRepository.getLists(organizationId) }

  fun getLists() : LiveData<Resource<MutableList<Something>>> {
    return lists
  }

  fun fetchLists(organizationId: String, forceRefresh: Boolean = false) {
    if (organizationIdLiveData.value == null || forceRefresh) {
      organizationIdLiveData.value = organizationId
    }
  }
}

I observe getLists() in my fragment, and call viewModel.fetchLists(id) when I want the data. Seems legit?

like image 76
Mathbl Avatar answered Sep 21 '22 14:09

Mathbl