8

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.

1 Answer 1

6

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)
        }
    }     
}
Sign up to request clarification or add additional context in comments.

1 Comment

How to observe this in a Fragment?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.