2

I am using the liveData coroutine as follows. My function takes 3 params - accessing database, make a API call and return the API result

fun <T, A> performGetOperation(
databaseQuery: () -> LiveData<T>,
networkCall: suspend () -> Resource<A>,
saveCallResult: suspend (A) -> Unit
): LiveData<Resource<T>> =
liveData(Dispatchers.IO) {
    emit(Resource.loading())
    val source = databaseQuery.invoke().map { Resource.success(it) }
    emitSource(source)

    val responseStatus = networkCall.invoke()
    if (responseStatus.status == SUCCESS) {
        saveCallResult(responseStatus.data!!)
    } else if (responseStatus.status == ERROR) {
        emit(Resource.error(responseStatus.message!!))
        emitSource(source)
    }
}

I am calling the function as

fun getImages(term: String) = performGetOperation(
    databaseQuery = {
        localDataSource.getAllImages(term) },
    networkCall = {
        remoteDataSource.getImages(term) },
    saveCallResult = {
        val searchedImages = mutableListOf<Images>()
        it.query.pages.values.filter {
            it.thumbnail != null
        }.map {
            searchedImages.add(Images(it.pageid, it.thumbnail!!.source, term))
        }
        localDataSource.insertAll(searchedImages)
    }
)

This is my viewmodel class

class ImagesViewModel @Inject constructor(
private val repository: WikiImageRepository
 ) : ViewModel() {

var images: LiveData<Resource<List<Images>>> = MutableLiveData()

fun fetchImages(search: String) {
    images = repository.getImages(search)
}
}

From my fragment I am observing the variable

viewModel.images?.observe(viewLifecycleOwner, Observer {
        when (it.status) {
            Resource.Status.SUCCESS -> {
                println(it)
            }
            Resource.Status.ERROR ->
                Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show()

            Resource.Status.LOADING ->
                println("loading")

        }
    })

I have to fetch new data on click of button viewModel.fetchImages(binding.searchEt.text.toString())

Function doesn't gets executed. Is there something I have missed out?

WISHY
  • 11,067
  • 25
  • 105
  • 197
  • I'm trying to move this file to the data layer but it's impossible, because it's pure java, and LiveData can't be imported. Any solution? – Patrick Aug 02 '22 at 09:20
  • @Patrick which file? Why can't live data be imported? – WISHY Aug 05 '22 at 05:23
  • performGetOperation function. I have a module Data, which is a java-library, and I cannot not import this function to this layer for the operations – Patrick Aug 07 '22 at 07:48

1 Answers1

1

The liveData {} extension function returns an instance of MediatorLiveData

liveData { .. emit(T) } // is a MediatorLiveData which needs a observer to execute

Why is the MediatorLiveData addSource block not executed ?

We need to always observe a MediatorLiveData using a liveData observer else the source block is never executed

So to make the liveData block execute just observe the liveData,

performGetOperation(
    databaseQuery = {
        localDataSource.getAllImages(term) },
    networkCall = {
        remoteDataSource.getImages(term) },
    saveCallResult = {
        localDataSource.insertAll(it)
    }
).observe(lifecyleOwner) { // observing the MediatorLiveData is necessary
}

In your case every time you call

images = repository.getImages(search)

a new instance of mediator liveData is created which does not have any observer. The old instance which is observed is ovewritten. You need to observe the new instance of getImages(...) again on button click.

images.observe(lifecycleOwner) { // on button click we observe again.
     // your observer code goes here
}

See MediatorLiveData and this

Santanu Sur
  • 10,997
  • 7
  • 33
  • 52
  • I am observing it in my fragment and I have to fetch new data on button click. Still the function doesn't gets executed, I have updated the question. Can you please take a look. – WISHY May 12 '21 at 09:32
  • but u are never observing `perforGetOperation(...)` which needs an observer. We need to observe `perforGetOperation(...)` atleast with a blank observer to make it execute as i have shown in the answer @WISHY – Santanu Sur May 12 '21 at 09:34
  • I am observing that from getImages fun – WISHY May 12 '21 at 09:36
  • on button click u need to observe again , its a separate instance now @WISHY it creates a new instance every time – Santanu Sur May 12 '21 at 09:38