P. Savrov
P. Savrov

Reputation: 1204

Kotlin Flow in repository pattern

I would like to use a Flow as a return type for all functions in my repository. For ex:

suspend fun create(item:T): Flow<Result<T>> 

This function should call 2 data sources: remote(to save data on the server) and local(to save returned data from the server locally). The question is how I can implement this scenario:

  1. try to save data with RemoteDataSource
  2. if 1. fails - try it N times with M timeout
  3. if data has finally returned from the server - same them locally with LocalDataSource
  4. return flow with locally saved data

RemoteDataSource and LocalDataSource both have fun create with the same signature:

suspend fun create(item:T): Flow<Result<T>> 

So they both return flow of data. If you have any ideas about how to implement it, I will be grateful.

------ Update #1 ------

a part of a possible solution:

suspend fun create(item:T): Flow<T> {
 // save item remotely
 return remoteDataSource.create(item)
  // todo: call retry if fails
  // save to local a merge two flows in one
  .flatMapConcat { remoteData ->
   localDataSource.create(remoteData)
  }
  .map {
   // other mapping
  }
}

Is it a working idea?

Upvotes: 4

Views: 6705

Answers (1)

CyrilFind
CyrilFind

Reputation: 804

I think you have the right idea but you are trying to do everything at once.

What I found works best (and easily) is to have:

  • an exposed flow of data coming from your local datasource (easy with Room)

  • one or more exposed suspend functions like create or refresh that operate on the remote data source and save to the local one (if there is no error)

For ex I have a repository that fetches vehicles in my project (the isCurrent info is only local and isLeft/isRight is because I use Either but any error handling applies):

class VehicleRepositoryImpl(
    private val localDataSource: LocalVehiclesDataSource,
    private val remoteDataSource: RemoteVehiclesDataSource
) : VehicleRepository {

    override val vehiclesFlow = localDataSource.vehicleListFlow
    override val currentVehicleFlow = localDataSource.currentVehicleFLow

    override suspend fun refresh() {
        remoteDataSource.getVehicles()
            .fold(
                ifLeft = { /* handle errors, retry, ... */ },
                ifRight = { reset(it) }
            )
    }

    private suspend fun reset(vehicles: List<VehicleEntity>) {
        val current = currentVehicleFlow.first()
        localDataSource.reset(vehicles)
        if (current != null) localDataSource.setCurrentVehicle(current)
    }

    override suspend fun setCurrentVehicle(vehicle: VehicleEntity) =
        localDataSource.setCurrentVehicle(vehicle)

    override suspend fun clear() = localDataSource.clear()
}

Hope this helps and you can adapt it to your case :)

Upvotes: 4

Related Questions