I'm seeking the best way to observe data in ViewModel
.
I'm using MVVM + DataBinding.
Repository:
private val data = MutableLiveData<String>()
suspend fun getData(): LiveData<String> {
return withContext(IO) {
val response = apiRequest { api.getData() }
data.postValue(response)
data
}
}
It requests data from server and returns a live data. ViewModel must observe the data changes.
ViewModel:
suspend fun getData() {
val data = repository.getData()
MediatorLiveData<String>().apply {
addSource(data) {
gotData(it)
removeSource(data)
}
observeForever { }
}
}
private fun gotData(data: String) {
//use data
}
ViewModel uses a MediatorLiveData
to observe changes of the LiveData
that comes from repository. I've added the data as a source to observe changes and remove it after it triggers to prevent firing events multiple times when I get data multiple times. And there must be a fake observer to MediatorLiveData
so the onChange method of the MediatorLiveData
triggers.
Let's say that I just need the data to hide/show a view (or even fill data to my recyclerview's adaper). Then I just call the below code and use an Observable and DataBinding like this:
val adapter: ObservableField<DataAdapter> = ObservableField()
val recyclerviewVisibility: ObservableField<Int> = ObservableField(View.GONE)
...
...
recyclerviewVisibility.set(View.VISIBLE)
adapter.set(DataAdapter(dataList))
So I don't need to pass the data to Fragment
or Activity
to use the viewLifecycleOwner
.
I also cannot use observeForever
in ViewModel
because it may fire onChange method multiple times in some situations.
Is there any better approach to get and observe data in ViewModel
?
Solution :
I've found out that the best way to reach my goal is to get the data from repository without using LiveData
:
Repository
suspend fun getData() : String{
return apiRequest { api.getData() }
}
ViewModel
suspend fun getData(){
val data = repository.getData()
gotData(data)
}
fun gotData(data: String) {
//use data
}
It's much simpler now.
Bonus:
extension:
fun <T : Any> ViewModel.request(request: suspend () -> (T), result: (T) -> (Unit) = {}) {
viewModelScope.launch {
result(request())
}
}
usage:
request({request.getData()}) {
//use data
}
reload.switchMap
shows me 'Unresolved Reference' error. soliveData(IO)
andemit
also show the same error. – Monteux