0
votes

So I am using flatMap with RxJava to chain some API calls that return Singles and was wondering how to pass a custom throwable to the subscribe method in the flatmap method depending on my own condition.

This is in an Android Kotlin app that uses MVVM and RxJava2/RxAndroid for data operations. I am trying to chain together a bunch of API calls that return Singles using the flatmap method. Everytime, I want to call a new API method after the previous one, I want to check to see if the previous call's response was successful. If the previous call's response was successful, I just call the next API method in the flatmap and everything is good, but if the previous call's response was not successful, I want to pass a custom throwable that tells me where and why the operation wasn't successful. Right now in the situation of an unsuccessful response, I pass a Single with a null value, but that just gives me a null pointer when subscribing and that isn't very helpful.

dataManager.apiCall1(dataManager.sessionId!!)
            .subscribeOn(schedulerProvider.io())
            .observeOn(schedulerProvider.ui())
            .flatMap{apiCall1Response ->
                if (apiCall1Response.isSuccessful && apiCall1Response.body() != null) {
                    // First api call was successful, execute api call 2.
                    return@flatMap dataManager.apiCall2(apiCall1Response.importantVal)
                } else {
                    // First api call failed 
                    Single.just(null)
                }
            }.flatMap{apiCall2Response ->
                if (apiCall2Response != null && apiCall2Response.isSuccessful && apiCall2Response.body() != null) {
                    // Second api call was successful, execute api call 3.
                    return@flatMap dataManager.apiCall3(apiCall2Response.importantVal)
                } else {
                    // Second api call failed
                    Single.just(null)
                }
            }.subscribe({apiCall3Response ->
                if (apiCall3Response != null && apiCall3Response.body() != null) {
                   // Success!
                   navigator!!.successful(response)
                } else {
                   // Third api call failed, handle error
                   navigator!!.handleError(Throwable("Api call 3 failed."))
                }
            }, {throwable ->
                // Failure, this is where I want to receive a custom throwable 
                // in case one of the responses were unsuccessful.
                navigator!!.handleError(throwable)
            })

I expect the throwable message to be something like "apiCall1" failed, but the throwable message when apiCall1 fails is just NullPointer because of the Single.just(null) that I pass.

3

3 Answers

3
votes

Instead of Single.just(null) use Single.error(). For example: Single.error<TypeHere>(RuntimeException("First api call failed"))

1
votes

For the most readable code, define an extension function like this

private fun <T> Single<Response<T>>.decorateWithErrorHandling(): Single<T> {
    return this
            .onErrorResumeNext { throwable ->
                // Map network layer exceptions, e.g. IOException to your specific "domain level" exceptions
                Single.error(mapNetworkErrorToSpecificThrowable(throwable))
            }
            .flatMap { response ->
                if (response.isSuccessful) {
                    Single.just(response.body())
                } else {
                    Single.error(convertToSpecificThrowable(response))
                }
            }
}

Use it like this:

apiCall1()
    .composeWithErrorHandling().flatMap { result1 -> apiCall2(result1) }
    .composeWithErrorHandling().flatMap { result2 -> apiCall3(result2) }

etc.

0
votes

Use this code

throw NullPointerException from flatMap

and catch the error by .onErrorReturn(throwable -> {//your work here})

@Carson J.