Search code examples
kotlinandroid-architecture-componentsandroid-livedata

How to propagate Livedata from Repository > ViewModel > Fragment


getMoreData() in ViewModel is called from outside of ViewModel, everytime user scroll to the bottom of RecyclerView.

fetchMore() in Repository returns a LiveData with LoadingStatus object which contains loading/success/failure and error message

How can I set the loadingStatus variable in ViewModel so that it can be observed properly by the Fragment?

Note: getMoreData() in ViewModel can be called multiple times as the user scrolls down.

ViewModel{
    val loadingStatus

    fun getMoreData(){
        repository.fetchMore()
    }
}

Repository{
    fun fetchMore() : LiveData<LoadingStatus>{

    }
}

Fragment{
    viewModel.loadingStatus.observe()
}

Solution

  • The issue is with the need for a Lifecycle Owner used for observations of LiveData in the Repository.

    First, you don't want to return a new LiveData<LoadingStatus> every time fetchMore() is called. That would create a new LiveData each time. Best case, you would want the function fetchMore() do some something like this, updating a single LiveData:

    Repository{
        val status = LiveData<LoadingStatus>()
    
        fun fetchMore() {
            status.postValue(LOADING)
    
            // Pseudo code for actually loading more items
    
            status.postValue(FINISHED LOADING)
        }
    }
    

    However, you will have the issue of observing the status from the ViewModel as it itself is not a Lifecycle implementation, so it cannot easily observe the LiveData from the Repository.

    My suggestion would be something like this:

    ViewModel{
        val loadingStatus: MutableLiveData<LoadingStatus>
    
        init{
            repository.observeStatus()
                .subscribe( event -> loadingStatus.postValue(event))
        }
    
        fun getMoreData(){
            repository.fetchMore()
        }
    }
    
    Repository{
        val status = BehaviorSubject.create<LoadingStatus>()
    
        fun observeStatus(): Observable<LoadingStatus> {
            return status
        }
    
        fun fetchMore(){
            status.onNext(LOADING)
    
            // Pseudo code for actually loading more items
    
            status.onNext(FINISHED LOADING)
        }
    }
    
    Fragment{
        viewModel.loadingStatus.observe()
    }
    

    Note that you will have to dispose the subscription in the ViewModel onCleared.
    Note that all of this is pseudo code and it should be done a lot cleaner than this.