Search code examples
androidviewmodelrepository-pattern

Android: business logic transformation in Repository/ViewModel


I have a repository class:

class RepositoryImpl(private val application: Application) :
    Repository {
    override suspend fun getCities(): Resource<List<City>> =
        try {
            val bufferReader = application.assets.open(CITIES_FILE_NAME).bufferedReader()
            val data = bufferReader.use {
                it.readText()
            }
            val gson = GsonBuilder().create()
            val type: Type = object : TypeToken<ArrayList<City?>?>() {}.type
            val fromJson = gson.fromJson<List<City>>(data, type)
            Resource.Success(fromJson)
        } catch (e: JsonSyntaxException) {
            Resource.Error(JSONSYNTAXEXCEPTION_ERROR_MESSAGE)
        } catch (e: IOException) {
            Resource.Error(IOEXCEPTION_ERROR_MESSAGE)
        }

and the Resource class is:

sealed class Resource<T>(
    val data: T? = null,
    val message: String? = null
) {
    class Success<T>(data: T) : Resource<T>(data)
    class Loading<T>(data: T? = null) : Resource<T>(data)
    class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
}

I need to fetch the cities and I do it in my VM like this:

class CityListViewModel(private val repository: Repository) : ViewModel() {
    @VisibleForTesting
    val allCities: LiveData<Resource<List<City>>> =
        liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Resource.Loading())
            val cities: Resource<List<City>> = repository.getCities().sortedBy { city: City -> city.name }
            emit(cities)
        }
}

The problem, is that I modeled my repository to wrap the list of cities in a Resource and I need to sort the cities alphabetically, so the line val cities: Resource<List<City>> = repository.getCities().sortedBy { city: City -> city.name } does not compile.

Am I doing it wrong like this? The repository just takes care of retrieving the data and wrapping it in the Resource and the business logic lies in the VM, but now it receives a Resource and needs to access the data, sort it, and put it back in the Resource so the Activity knows what to do depending on the whether it's a Success, Error or Loading.

Thanks a lot!


Solution

  • You can map your data before emiting to the UI:

    ...
    emit(Resource.Loading())
    val resource = repository.getCities().map {
        if (it is Resource.Success) {
            Resource.Success(it.data.sortedBy { city: City -> city.name })
        } else {
            it
        }
    }
    emit(resource)
    ...