I am using viewModelScope
in the ViewModel
which calls a suspend function in the repository as shown below:
ViewModel
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
var deepFilterLiveData: LiveData<Result>? = null
fun onImageCompressed(compressedImage: File): LiveData<Result>? {
if (deepFilterLiveData == null) {
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
}
return deepFilterLiveData
}
}
Repository
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File): LiveData<Result> {
val mutableLiveData = MutableLiveData<Result>()
mutableLiveData.value = Result.Loading
withContext(Dispatchers.IO) {
mutableLiveData.value = Result.Success("Done")
}
return mutableLiveData
}
}
I am observing the LiveData from the Fragment as shown below:
viewModel.onImageCompressed(compressedImage)?.observe(this, Observer { result ->
when (result) {
is Result.Loading -> {
loader.makeVisible()
}
is Result.Success<*> -> {
// Process result
}
}
})
The problem is I am getting no value from the LiveData. If I don't use viewModelScope.launch {}
as shown below, then everything works fine.
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
var deepFilterLiveData: LiveData<Result>? = null
fun onImageCompressed(compressedImage: File): LiveData<Result>? {
if (deepFilterLiveData == null) {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
return deepFilterLiveData
}
}
I don't know what I am missing. Any help will be appreciated.
This code:
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
returns immediately so when you first invoke the onImageCompressed()
method you return null
as deepFilterLiveData
. Because in your UI you use ?.
on the null
return value of onImageCompressed()
the when
clause will not be reached. The code without the coroutine works because in that case you have sequential code, your ViewModel awaits for the repository call.
To solve this you could keep the LiveData for the ViewModel-UI interaction and return the values directly from the repository method:
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File) = withContext(Dispatchers.IO) {
Result.Success("Done")
}
}
And the ViewModel:
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
private val _backingLiveData = MutableLiveData<Result>()
val deepFilterLiveData: LiveData<Result>
get() = _backingLiveData
fun onImageCompressed(compressedImage: File) {
// you could also set Loading as the initial state for _backingLiveData.value
_backingLiveData.value = Result.Loading
viewModelScope.launch {
_backingLiveData.value = repo.applyFilter(compressedImage)
}
}
}