Search code examples
androidandroid-fragmentsandroid-architecture-componentsandroid-viewmodelandroid-mvvm

How to wait until all viewmodel asynchronous operations are done


I need to execute something in my view after all of the asynchronous fetches has been done , for this I have a home framgnet that fetches 3 different sections in my backend

  private fun populateSectionA(){
        viewModel.fetchSectionA.observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Loading -> { //Handling loading }

                is Resource.Success -> {
                  //Handles data
                }
                is Resource.Failure -> { //Error handling }
            }
        })
    }

   private fun populateSectionB(){
        viewModel.fetchSectionB.observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Loading -> { //Handling loading }

                is Resource.Success -> {
                  //Handles data
                }
                is Resource.Failure -> { //Error handling }
            }
        })
    }

   private fun populateSectionC(){
        viewModel.fetchSectionC.observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Loading -> { //Handling loading }

                is Resource.Success -> {
                  //Handles data
                }
                is Resource.Failure -> { //Error handling }
            }
        })
    }

Here are my observers in my view, what I need to know is when all of these 3 has finished to load my UI

From my viewmodel the fetch is the same for the three sections

    val fetchSectionA = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
                emit(Resource.Loading())
                try {
                    emit(repo.fetchSectionA(sectionId))
                } catch (e: Exception) {
                    emit(Resource.Failure(e))
                }
            }

 val fetchSectionB = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Resource.Loading())
            try {
                emit(repo.fetchSectionB(sectionId))
            } catch (e: Exception) {
                emit(Resource.Failure(e))
            }
        }

 val fetchSectionC = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Resource.Loading())
            try {
                emit(repo.fetchSectionC(sectionId))
            } catch (e: Exception) {
                emit(Resource.Failure(e))
            }
        }

Now, how can I know when all of these individual fetches has been done ? I dont want to use a counter in my UI that counts to certain number untill its reached, instead I would love to give a callback to my UI when all these operations finishes


Solution

  • If you have the following code:

    fun <T1, T2, T3> combineTuple(f1: LiveData<T1>, f2: LiveData<T2>, f3: LiveData<T3>): LiveData<Triple<T1?, T2?, T3?>> = MediatorLiveData<Triple<T1?, T2?, T3?>>().also { mediator ->
        mediator.value = Triple(f1.value, f2.value, f3.value)
    
        mediator.addSource(f1) { t1: T1? ->
            val (_, t2, t3) = mediator.value!!
            mediator.value = Triple(t1, t2, t3)
        }
    
        mediator.addSource(f2) { t2: T2? ->
            val (t1, _, t3) = mediator.value!!
            mediator.value = Triple(t1, t2, t3)
        }
    
        mediator.addSource(f3) { t3: T3? ->
            val (t1, t2, _) = mediator.value!!
            mediator.value = Triple(t1, t2, t3)
        }
    }
    

    Then you can do:

    combineTuple(fetchSectionA, fetchSectionB, fetchSectionC)
        .map { (sectionA, sectionB, sectionC) ->
            val sectionA = sectionA.takeIf { it != Resource.Loading } ?: return@map null
            val sectionB = sectionB.takeIf { it != Resource.Loading } ?: return@map null
            val sectionC = sectionC.takeIf { it != Resource.Loading } ?: return@map null
    
            return Triple(sectionA, sectionB, sectionC)
        }
    

    If you need more or less arity combiners for LiveData, check https://github.com/Zhuinden/livedata-combinetuple-kt