Search code examples
androidfirebaseandroid-fragmentskotlin

ViewModel prevent refetching of data


My viewModel refetches my data causing my shimmer to keep executing each time I get to this fragment, and also the data is beign refetched which is making my bill goes up on Firebase

How can I prevent to refetch again the data each time my fragment pops up ?

From my view

 viewModel.fetchShops(location).observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Loading -> {
                    shimmer.visibility = View.VISIBLE
                    shimmer.startShimmer()}
                is Resource.Success -> {
                    shimmer.visibility = View.GONE
                    shimmer.stopShimmer()
                    adapter.setItems(it.data)
                }
                is Resource.Failure -> {
                    Toast.makeText(requireContext(),"Error fetching data",Toast.LENGTH_SHORT).show()
                }
            }
        })

I call this in my onViewCreated() so each time this fragment recreates my Resource.Loading fires as well as the fetchShops(location) and this fetches again to my database, I want it to fetch just once each time I come back to this fragment, any idea?

ViewModel

   fun fetchShops(location:String) = liveData(Dispatchers.IO) {
            emit(Resource.Loading())
            try{
                emit(repo.fetchShops(location))
            }catch (e:Exception){
                emit(Resource.Failure(e))
            }
        }

Solution

  • You're creating a new LiveData instance every time fetchShops() is called. This means that any previously created LiveData (and the previous value it stores) is lost.

    Instead, you should Transform your LiveData by using your location as the input into creating your liveData { } block as per the liveData with Transformations .

    private val locationQuery = MutableLiveData<String>
    
    // Use distinctUntilChanged() to only requery when the location changes
    val shops = locationQuery.distinctUntilChanged().switchMap { location ->
        // Note we use viewModelScope.coroutineContext to properly support cancellation
        liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Resource.Loading())
            try{
                emit(repo.fetchShops(location))
            }catch (e:Exception){
                emit(Resource.Failure(e))
            }
        }
    }
    
    fun setLocation(location: String) {
        locationQuery.value = location
    }
    

    You can then use this by updating your Fragment:

    // Set the current location
    viewModel.setLocation(location)
    
    // Observe the shops
    viewModel.shops.observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Loading -> {
                    shimmer.visibility = View.VISIBLE
                    shimmer.startShimmer()}
                is Resource.Success -> {
                    shimmer.visibility = View.GONE
                    shimmer.stopShimmer()
                    adapter.setItems(it.data)
                }
                is Resource.Failure -> {
                    Toast.makeText(requireContext(),"Error fetching data",Toast.LENGTH_SHORT).show()
                }
            }
        })