androidobservablerx-java2repository-patternrx-kotlin2

RxJava ConcatArrayDelayError and filters: returning an error only if both sources fail


I'm new to RxJava and after a few days of trying everything I could find online I see that I really need help with this one.

I fetch a member in my repository with local and remote sources. I added some operators to return my remote source in priority (via debounce), and to filter out errors so it would return only 1 of the 2 if either remote is not available or the database is empty.

It works fine as long as something is returned by one of my 2 sources, but the problem occurs if both sources returns errors: as I filter out the errors, it doesn't return anything, and my subscribe is never called.

Maybe there is a simple solution but I have not found it so far, could someone help?

Here is my fetchMember() in my Repository:

override fun fetchMember(): Observable<MemberModel?> {
    return Observable.concatArrayDelayError(memberLocalSource.fetchMember(), memberRemoteSource.fetchMember())
            .doOnNext { member ->
                saveMember(member!!)
            }
            .materialize()
            .filter { !it.isOnError }
            .dematerialize { it -> it }
            .debounce(400, TimeUnit.MILLISECONDS)
   }

And here is my viewmodel:

fun fetchToken(username: String, password: String) {
    val loginDisposable = authApiService.loginWithJWT(username, password)
            .flatMap {
                isAuthenticated = isTokenValid(username, password, it)
                sharedPreferences.setHasValidCredentials(isAuthenticated)
                memberRepository.fetchMember()
            }
            .subscribeOn(Schedulers.io())
            .observeOn((AndroidSchedulers.mainThread()))
            .doOnError { throwable ->
                throwable.printStackTrace()
            }
            .subscribe(
                    { member -> 
                        memberLiveData.value = member
                        this.memberId = member!!.id.toString()
                        this.memberName = member.name.split(" ")[0]
                        if(isAuthenticated) {
                            authenticationState.value = AuthenticationState.AUTHENTICATED_VALID_MEMBER
                        } else {
                            authenticationState.value = AuthenticationState.UNAUTHENTICATED_VALID_MEMBER
                        }
                    },
                    { error ->
                        if(isAuthenticated) {
                            authenticationState.value = AuthenticationState.AUTHENTICATED_INVALID_MEMBER
                        } else {
                            authenticationState.value = AuthenticationState.INVALID_AUTHENTICATION
                        }
                    })
    disposable.add(loginDisposable)
}

private fun isTokenValid(username: String, password: String, authResponse: AuthModel): Boolean {
    return if (authResponse.data != null) {
        false
    } else {
        tokenInterceptor.token = authResponse.token
        val tokenWithCredentials = AuthModel(authResponse.token, null, null, username, password)
        tokenRepository.saveToken(tokenWithCredentials)
        true
    }
}

Solution

  • In the end I managed to make it work by adding:

     .defaultIfEmpty(MemberModel(-1)) 
    

    and checking against id == -1.