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)
}