Home > front end >  Good solution for call one method from ViewModel multiple times
Good solution for call one method from ViewModel multiple times

Time:10-25

I need to call multiple times one method from MVVM for API call and result sent into recycler view. I can't know how much times user will need to do that because it's depends on list size. For example:

list: ["FirstElement", "SecondElement", "..", "..."]

in code I was try call method like that in loop:

 for (city in list) {
     viewModel.getCurrentWeatherByCity(city, appID, unit).observe(viewLifecycleOwner, {
     result.add(it) // here is arraylist for recyclerview
   })
 }
 val adapterRecycler = LocationListWeatherRecyclerAdapter(result) // init adapter for recycler
 setUpRecyclerData(adapterRecycler) // method for setup recyclerview

View Model method:

fun getCurrentWeatherByCity(city: String, appID: String, units: String): LiveData<WeatherModel> {
 return repository.getCurrentWeatherByCity(city, appID, units)
}

and repository method:


    fun getCurrentWeatherByCity(city: String, appID: String, units: String ) : LiveData<WeatherModel> {
        apiService.getCurrentWeatherByCity(city, appID, units).enqueue(object : Callback<WeatherModel>{
            override fun onResponse(call: Call<WeatherModel>, response: Response<WeatherModel>) {
                if (response.isSuccessful) {
                    weatherData.postValue(response.body())
                } else {
                }
            }

            override fun onFailure(call: Call<WeatherModel>, t: Throwable) {
            }
        })

        return weatherData
    }

but I know it's wrong solution because result is doing in background and wrong. This for can return 80 items in array from 4 iterations in loop.

Do you know what I can do instead this? Thanks for help

CodePudding user response:

This can be solved more easily using suspend functions, which is how I would do it.

First make your repo use a suspend function that return WeatherModel directly instead of a LiveData<WeatherModel>. It can return null on failure, or you could do something more sophisticated if you want.

suspend fun getCurrentWeatherByCity(city: String, appID: String, units: String ) : WeatherModel? {
    return runCatching { apiService.getCurrentWeatherByCity(city, appID, units).await() }
        .getOrNull()
}

Then you can create a function in your ViewModel that accepts a list of cities and runs the api calls in parallel, like this:

fun getCurrentWeatherByCities(cities: List<String>, appID: String, units: String) = liveData<List<WeatherModel>> {
    val weatherModels = cities.map { city -> 
        async { repository.getCurrentWeatherByCity(city, appID, units) }
    }
        .awaitAll()
        .filterNotNull()
    emit(weatherModels)
}

Then in your fragment, observe it and set the adapter list in the observer.

viewModel.getCurrentWeatherByCities(cities, appID, unit).observe(viewLifecycleOwner, {
    val adapterRecycler = LocationListWeatherRecyclerAdapter(it)
    setUpRecyclerData(adapterRecycler)
}
  • Related