Search code examples
androidkotlinrepository-patternrx-kotlin

Repository pattern concatenating results in RXKotlin


Trying to come with a offline-first app, I managed to make a working repository pattern which first check the cache, then the database and finally, the api. I do that like this:

private fun cachedTopics(): Maybe<List<Topic>> { ... }
private fun databaseTopics(): Maybe<List<Topic>> { ... }
private fun apiTopics(): Maybe<List<Topic>> { ... }

Maybe.concat(cachedTopics(), databaseTopics(), apiTopics())
     .firstOrError()
     .subscribeOn(scheduler)

Now, the thing is that I save partial data in the database (not all topics). If a user is connected to internet and browse the data displayed will only be the data in the database. But that's not all the content a user should access (if they are connected to internet). I'm aware this is due to the .firstOrError() call.

But I wonder if there is a way to concatenate the distinct results of database + api (api might fail and thus return 0 results) and return a Single.

Edit

To be clearer I want something like the following:

IF CACHE IS NOT EMPTY
   RETURN DISTINCT(DB + CACHE)
ELSE
   UPDATE CACHE WITH API RESULT
   RETURN DISTINCT(DB + API)

Where API calls will automatically result in having nor results if it fails.


Solution

  • Perhaps what you want here is Maybe#switchIfEmpty (docs here)

    Then you could do something like this:

    class Repository() {
    
        fun topics() {
            return cachedTopics.switchIfEmpty(databaseOrApiTopics());
        }
    
    
        private fun databaseOrApiTopics() = databaseTopics().switchIfEmpty(apiTopicsWithUpdate())
    
        private fun apiTopicsWithUpdate() = apiTopics().doOnComplete {
            // update db
            // update cache?
        }
    }
    

    UPDATE: if you want to combine results together, you can do this with the overload of Maybe.flatMap that takes a combiner. Something like:

    cachedTopics().switchIfEmpty(
        databaseTopics().flatMap { dbTopics ->
            apiTopics().doOnComplete { /* updateDb(it) */ }
                .map { apiTopics -> apiTopics + dbTopics }
        }
    )