Search code examples
androidkotlinrepositorykotlin-flow

Kotlin Flow Offline Caching


I am new with kotlin flow and I am working about this document. Kotlin Flows. In this code every five seconds datasource fetch data from api and emits it.

This is my example datasource class.

I am getting data and emitting it.

class RemoteDataSourceImpl @Inject constructor(
private val api:CryptoApi
): RemoteDataSource {


override suspend fun cryptoList(): Flow<List<CryptoCoinDto>> {
    return flow {
        while (true){
            
            val data = api.getCoinList()
            emit(data)
            delay(5000L)
        }
    }
   }
}

This is my example repository.

I am mapping data and saving it room database. I want to get data from room database and emit it because of single source of truth principle but I still have to return dataSource because if I open new flow{} I can't reach datasource's data. Of course I can fix the problem by using List instead of Flow<List> inside of RemoteDataSource class. But I want to understand this example. How can I apply here single source of truth.

class CoinRepositoryImpl @Inject constructor(
private val dataSource:RemoteDataSource,
private val dao: CryptoDao
):CoinRepository {

override fun getDataList(): Flow<List<CryptoCoin>> {

     dataSource.cryptoList().map { dtoList ->
        val entityList = dtoList.map { dto ->
            dto.toCryptoEntity()
        }
        dao.insertAll(entityList)
    }
    return dataSource.cryptoList().map {
        it.map { it.toCryptoCoin() }
    }

}

Solution

  • Maybe this can help. I personally wouldn't return a Flow for the API stuff since you are technically speaking returning only 1 list all the time

    override fun getDataList(): Flow<List<CryptoCoin>> {
        val localFlow = dao.getCryptoCoins()
        val apiFlow = flow<Unit> {
            // Since getDataList can't be suspend due to it returning a flow, we create a new flow simply to run a suspendable function
            getAndSaveCryptoCoinsFromApi()
        }.onStart { // If I don't add this onStart, then it won't work
            emit(Unit)
        }
        
        return localFlow.combine(apiFlow) { dbItems, _ ->
            dbItems
            // We just return DB items. Why? Since we are saving api items to the db, and since the db items return a flow, it will automatically emit values to the flow once new items are added to the db
        }
    }
    
    private suspend fun getAndSaveCryptoCoinsFromApi() {
        try {
            val result = api.getCryptoCoins().toDomain()
            val entities = result.map { it.toEntity() }
            dao.insertCoins(entities)
        } catch(e: Exception) { /**/ }
    
    }