Hi guys I need help are there any better way to re...
# android
o
Hi guys I need help are there any better way to refactor this code ? It's repository pattern, so i check data from db first if there is empty then fetch from network cache it and fetch the data from db and then return it if there is found just return it. so are there any better way to implement this ?
Copy code
override fun findUserByUsername(username: String): Flow<Resource<List<UserEntity>>> =
        localDataSource.findUserByUsername(username).map { fromDb ->
            when (fromDb) {
                is FromDb.Empty -> {
                    when (val fromNetwork = remoteDataSource.findUsersByUsername(username)) {
                        is FromNetwork.Error -> Resource.Error(fromNetwork.message)
                        is FromNetwork.Success -> {
                            val userCache = fromNetwork.data.map { it.toEntity() }

                            localDataSource.insertUsers(userCache)

                            var userFromDb: Resource<List<UserEntity>> = Resource.Loading()
                            
                            localDataSource.findUserByUsername(username).collect {
                                userFromDb = when (it) {
                                    is FromDb.Empty -> Resource.Error(it.message)
                                    is FromDb.Success -> Resource.Success(it.data)
                                }
                            }
                            
                            userFromDb
                        }
                    }
                }
                is FromDb.Success -> Resource.Success(fromDb.data)
            }
        }.onStart {
            Resource.Loading<List<UserEntity>>()
        }.flowOn(dispatcher)
When I try to use .map it shows error like in this photo
e
You are returning a flow of resources of list of user entities, you are not to return the any resource class but you are meant to return a flow, and emit all the resources classes in the flow, so instead of setting it to be "= localDataSource.findUserByUsername" use " = flow { //all your code, and you'll have to emit all the resource classes with emit(Resource.Whatever) }
What you are returning from your localdatasource is a flow but not of the resources class
s
first of all you should spilt the calls, to eg. loadUserFromDb, loadUserFromServer and then something to wrap it up like loadUser. Then inside loadUser you always first look into the db, and if nothing is in there, you make the call to the server. I guess the user is waiting for this to finish, so you could add some intermediate flow when the call to the server is needed since this may take a lot longer than the local db call
o
Thank you guys for your answer
Copy code
override fun findUserByUsername(username: String): Flow<Resource<List<UserEntity>>> = flow {
        localDataSource.findUserByUsername(username).collect { local ->
            if (local.isNullOrEmpty()) {
                remoteDataSource.findUsersByUsername(username).collect { remote ->
                    if (remote.isNullOrEmpty()) emit(Resource.Empty("Data is not found"))

                    if (remote.isNotEmpty()) {
                        val caches = remote.map { it.toEntity() }
                        localDataSource.insertUsers(caches)

                        localDataSource.findUserByUsername(username).collect {
                            emit(Resource.Success(it))
                        }
                    }
                }
            }
            emit(Resource.Success(local))
        }
    }.onStart {
        emit(Resource.Loading())
    }.onEmpty {
        emit(Resource.Empty("Data is not found"))
    }.catch {
        Log.d("repo findUser", "$it")
    }.flowOn(dispatcher)
I've implemented it this way while waiting you guys answer this question, so are there any better way than this
e
There is no need of returning a flow from you local data source if that particular row doesn't change and even if it does your ui will never know because it is returning a cold flow except you're changing it to a livedata somewhere, I'll recommend returning just a Dog object from your local data source and the same goes for your remote data source, it's not necessary also of your ui only calls the method once and some other times because Flow.collect is triggered only when the method is called in your ui or view model that's why it's cold and I'm guessing remote sources requests are one-shot-operations so there is no need to return a flow from your remote data source, just return objects to reduce your number of callbacks😖, remove unnecessary code and save resources.
For remote data sources, you could follow my tip, except you are integrating flows and sockets somehow with the backend