I’m trying to observe the loading state of a page. However, I make 2 API calls on my ViewModel. I want to display a progress bar until both of the items are loaded.
I have a sealed class to indicate the loading state of the data, which goes like:
sealed class DataState<out R> {
data class Success<out T>(val data: T) : DataState<T>()
data class Error(val exception: Exception) : DataState<Nothing>()
object Loading : DataState<Nothing>()
}
My view model:
init {
getData1()
getData2()
}
val data1 = MutableLiveData<List<model1>>()
val data2 = MutableLiveData<List<model2>>()
private fun getData1() {
viewModelScope.launch {
data1.postValue(DataState.Loading)
val result = try {
repository.getData1().data!!
}
catch (e: Exception) {
data1.postValue(DataState.Error(e))
return@launch
}
data1.postValue(DataState.Success(result))
}
}
private fun getData2() {
viewModelScope.launch {
data2.postValue(DataState.Loading)
val result = try {
repository.getData2().data!!
}
catch (e: Exception) {
data2.postValue(DataState.Error(e))
return@launch
}
data2.postValue(DataState.Success(result))
}
}
I wanna observe a live data so that I can see that both of the states are successful. Is that possible?
2
Answers
You probably want a
MediatorLiveData
:That’s the example from the docs – it’s a very simple one that just sets a single value on
liveDataMerger
when either of the source LiveDatas posts a new value.There’s an example on the Android Developers blog that’s closer to what you want:
so every time you get a new value on one of the LiveDatas, you pass them both to a function that does some validation and returns a current state.
I should also point out that
Flow
s are recommended for a lot of things now, and thecombine
function (which does the same kind of thing asMediatorLiveData
) is a bit easier to read (#5 in that article). Just so you know! Either is good hereYou don’t need to have separate
LiveData
ofDataState
for each API Call.Rather than this:
You can have just this:
Consider
model3
as adata class
that aggregates bothmodel1
andmodel2
. It can be used to aggregate other data fields that can be used in future.This way, when you observe the states, maintaining states becomes simpler:
One more thing, since the methods
getData1()
andgetData2()
are launching Coroutines separately, the REST API Calls are not in sequence of one another. And it’s not guaranteed which call would finish first, that can lead to extra code just to maintain the progress until both are completed.This can be solved easily by sequencing the REST API Calls itself, by merging the data fetch operation into one method under one
CoroutineScope
.To summarize: