For a while now I haven’t been completely satisfied with how I was handling REST Api errors. It seemed messy and confusing; never knowing completely how to handle the errors in a clean concise way with very few documented solutions.

That is when I stumbled across a code base that contained a nice solution using RxJava. I’m not going to lie, it took me a couple of hours to get my head around what was going on but in the end I came out the other side more comfortable with RxJava so I think it’s well worth trying to understand other peoples code even if it at first it looks overly complex.

TL;DR

RxJava is cool. Kotlin is amazing. Retrofit is fantastic. Github repo with code here. Thanks for coming.

Simple Retrofit Api Call

You may have done this many times with Retrofit; defining an API call that returns an Observable. This may look like so in your API interface class —oh btw, this example is in Kotlin although you can do exactly the same thing in Java just in a more bloated and ugly way.

interface MovieDbApi {



@GET("movie/popular")

fun getPopularMovies(@Query("page") page: Int?): Observable<PopularMoviesResponse>



}

We have our endpoint which looks great. Now we need to use this endpoint.

ViewModels and Subjects

I, like a lot of people, enjoy clean code and making that code as testable as possible. For this reason I either use the MVVM or MVP pattern. For the purposes of this example and the fact that it’s only very recently come out, I will be using the new ViewModel architecture component provided by Google although I won’t be explaining in depth this component.

The Trigger

Now the scenario is this. I have a button on my view, which when pressed, will makea get request to the API. How do we go about this in a reactive way?

From my ViewModel I expose an Observer which is a PublishSubject like so:

fun getPopularMoviesRefreshObserver() : Observer<Unit> {

return refreshSubject

}

Then from my Activity I call this Subject’s onNext() when I want it triggered like so.

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)



btn_ma_popular_movies.setOnClickListener {

viewModel.getPopularMoviesRefreshObserver().onNext(Unit)

}

}

The Api Call

In it’s most basic form, which we will expand later on, here is an Observable that will emit when the refreshSubject’s onNext is called. As you can see, for now we’re limiting it to only the first page of results.

private val basicApiCall: Observable<List<MovieSummary>>



init {

basicApiCall = refreshSubject

.flatMap { return@flatMap movieDbApi.getPopularMovies(1)

.subscribeOn(Schedulers.io())

.map { it.results } }

}

So in the above code I am taking my refreshSubject , passing it’s emission to the movieDbApi.getPopularMovies() observable and making sure only the list of results gets returned rather than the response wrapper.

This Observable is retrieved from the view like so:

override fun bindViewModel() {

viewModel.getPopularMoviesRetrievedObservable()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribeBy(

onNext = {

it.forEach { Log.d("MainActivity","Movie found: $it") } },

onError = {

Log.e("MainActivity","Api call error",it)})

}

Perfect. We are getting responses from the Api with a list of movies being printed each time the button is pressed.

Errors!

With out current configuration we have an issue. If an error occurs, such as with Retrofit throwing an HttpException or there being a SocketTimeoutException due to a poor internet connection, this observable will no longer work! Button presses after the onError is called will not cause the basicApiCall to emit any items because it’s stream as been terminated.

We could return a new Observable every time the view binds to the ViewModel but this would not work for our code later on so let’s get it working this way. To do this we wrap any errors thrown by our Api call in an object.

open class Result<T>( val data: T? = null, val error: Throwable? = null) {

companion object {

fun <T> fromData( data: T ) : Result<T> {

return Result(data, null)

}



fun <T> fromError( error : Throwable ) : Result<T> {

return Result(null, error)

}

}



fun isError() : Boolean{

return error != null

}



fun isSuccess() : Boolean {

return data != null

}

}

This is the wrapper object. This can be used so that we can still retrieve our errors but the observable also continues emitting items. We use it like so:

basicApiCall = refreshSubject

.flatMap { return movieDbApi.getPopularMovies(1)

.map { Result.fromData(it.results) }

.onErrorResumeNext( Function { Observable.just(Result.fromError(it)) })

.subscribeOn(Schedulers.io())

Notice the map on the getPopularMovies observable. This is the best case and if it reaches this point the Api call was successful. However, if an error is thrown, thats when the onErrorResumeNext operator comes into play. This basically says, if a throwable is emitted then return this Observable instead.

But this looks ugly I hear you say

This is Kotlin after all, there must be a way to make it look good! Check this addition to the Result class out. NB- this is outside of the Result class but inside the same file.

fun <T> Observable<T>.toResult() : Observable<Result<T>> {

return map { Result.fromData(it) }

.onErrorResumeNext( Function { Observable.just(Result.fromError(it)) })

}

This is one of my favourite features in Kotlin; extensions. It allows you to extend classes you wouldn’t normally have control over and allows you to do cool things like this.

basicApiCall = refreshSubject

.flatMap { return@flatMap movieDbApi.getPopularMovies(1)

.map { it.results }

.toResult()

.subscribeOn(Schedulers.io())}

Yes we had to add a line to map our emission to a list of movies but the .toResult() is a lot nicer to read than the previous example.

Our View then updates like so to show the results:

return viewModel.getPopularMoviesRetrievedObservable()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe {

it.data?.forEach {

Log.d("MainActivity","Movie found: $it") }

it.error?.let{Log.e("MainActivity","Api call error", it)}

}

Get Down to Business

So our Api call can now be triggered multiple times, even after an error occurs. However, to handle individual error codes there would have to be far too much business logic in the view. Now lets get more involved with RxJava!

So I’m happy with our basic Api call observable that returns a Result<List<MovieSummary> . The problem is I want to share the emission of these items with other observables. To do this we turn our regular Observable into a ConnectableObservable by adding the .publish() parameter onto the end.

val basicApiCall

= refreshSubject

.flatMap { return@flatMap movieDbApi.getPopularMovies(1)

.map { it.results }

.toResult()

.subscribeOn(Schedulers.io())}

.publish()

This basicApiCall Observable we have been using will no longer be accessible by the user. Instead, we want to give the view access, only to specific Observables that correspond to certain states we expect. Take a successful response for example. We know there will be no exception thrown and a list of MovieSummary objects will be returned. Let’s create an Observable just for this.

fun getPopularMoviesRetrievedObservable() : Observable<Result<List<MovieSummary>>> {

return basicApiCall.filter { it.isSuccess() }

}

Now we can guarantee that when this observable fires, it’s only going to be because it received a successful response from the Api and the view only has to handle this scenario.

return viewModel.getPopularMoviesRetrievedObservable()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe{ it.data?.forEach { Log.d("MainActivity","Movie found: $it") }

Let’s handle an error.

When a page of value 0 is passed to the endpoint, it returns with an HttpException that has a code of 424 . This is a good one to handle as we can guarantee the response each time, however it’s real world applications may not be as useful.

fun getPopularMoviesPageErrorObservable() : Observable<Unit> {

return basicApiCall.filter { it.isError() }

.filter { it.error is HttpException }

.map { it.error as HttpException }

.filter { it.code() == 424 }

.map { Unit }

}

As you can see we are sharing the basicApiCall , ConnectableObservable in the same way the success Observable does. This one however only emits an item if an HttpException with the code 424 is emitted.

But hold on! That’s ugly! Let’s add another few extensions to our Result.kt file, but not in the class, to make our code nicer.

fun <T> Observable<Result<T>>.onlySuccess() : Observable<T> {

return filter { it.isSuccess() }

.map { it.data!! }

}



fun <T> Observable<Result<T>>.onlyError() : Observable<Throwable> {

return filter { it.isError() }

.map { it.error!! }

}



fun <T> Observable<Result<T>>.onlyHttpException() : Observable<HttpException> {

return filter{ it.isError() && it.error is HttpException}

.map { it.error as HttpException }

}



fun <T> Observable<Result<T>>.onlyHttpException(code: Int) : Observable<HttpException> {

return onlyHttpException()

.filter { it.code() == code }

}



fun <T> Observable<Result<T>>.onlyHttpExceptionExcluding(vararg codes: Int) : Observable<HttpException> {

return onlyHttpException()

.filter { codes.contains(it.code()) }

}

Now our specific page error observable becomes:

fun getPopularMoviesPageErrorObservable() : Observable<Unit> {

return basicApiCall.onlyHttpException(424)

.map { Unit }

}

And our successful observable becomes:

fun getPopularMoviesRetrievedObservable() : Observable<List<MovieSummary>> {

return basicApiCall.onlySuccess()

}

Generic Error Handling

We have handled the case of catching specific errors now let’s catch all the others!

fun getPopularMoviesGenericErrorObservable() : Observable<Unit> {

return basicApiCall.onlyHttpExceptionExcluding(424)

.map { Unit }

}

Connecting to the View

We have already connected the successful observable to our view (which now requires some modification after the extension addition) , now lets hook up the errors.

override fun bindViewModel() {

viewModel.getPopularMoviesRetrievedObservable()

.bindToLifecycle(this@MainActivity)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe { it.forEach { Log.d("MainActivity","Movie: $it") } }

viewModel.getPopularMoviesPageErrorObservable()

.bindToLifecycle(this@MainActivity)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe { Log.d("MainActivity","Page of 0 error") }

viewModel.getPopularMoviesGenericErrorObservable()

.bindToLifecycle(this@MainActivity)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe { Log.d("MainActivity","Something went wrong") }

}

Notice the bindToLifecycle() method. This is so we don’t leak our disposables using a library called RxLifecycle.

We subscribe to each observable on the io thread so it runs in the background and we observe on the main thread so we can do things to our Ui when items are emitted.

For now these are just logging the different errors and responses but you can see how you can quickly hook this up to different parts of your Ui.

Conclusion

I much prefer this way of handling my Api requests. Everything is separated out with clear and distinct observables for different scenarios. Business logic is also far removed from the view.

All the code from this post can be found here — https://github.com/StuStirling/moviedb-apihandling/tree/9fd8d6f5a0f01124a743d1731d18f094e1485b50