Coroutines, Retrofit, and a nice way to handle Responses

Guilherme Delgado
Level Up Coding
Published in
3 min readApr 7, 2020

--

Photo by Mario Caruso on Unsplash

Before we start, I assume the reader has knowledge and base concepts of Kotlin, Coroutines, ReactiveX, and Android App Architecture.

One-shot async calls

With coroutines becoming more popular theses days I’ve decided to update my ViewModel-Repository-DataSource communication logic and change it from ReactiveX to Coroutines. There’s nothing wrong with Rx, actually, Kotlin has data streams - checkout Flow and Channel -, but I completely agree with what Daniel Lew said in his Grokking Coroutines talk:

“…concurrency doesn’t have anything to do with streams of data.”

How many of us have implemented a scenario where “one-time fetch operation” is used to return data, complete, or throw an error:

Architecture based on stream events

What we really intend for is an asynchronous task that will fetch data, maybe perform some operations and finally notify us when ready. We don’t need a Single or Completable for that.

Bearing in mind that I have already successfully implemented this approach in a few projects, I decided to write about it.

Asynchronous architecture

First, let’s draw our architecture. We’ll have a BaseService responsible for handling network calls, successes and errors. ChildServices will extend BaseService. Our Repository will communicate with ChildServices and manage all the information before returning it back to the ViewModel with a Response mapped to ViewModel’s world.

BaseService for asynchronous architecture

Api and ChildServices are pretty straight forward:

Api and ChildService for asynchronous architecture

note: ApiService it’s an abstraction layer to better isolate API logic from BaseService.

Now the fun part, Repository. In our BaseService we are returning a Result<T> with either a Success or an Error:

Thus, that’s what we’ll be returning in our Repository too:

And for the last piece of the puzzle, the ViewModel (child):

And that’s it!

You might have noticed that my repository calls are wrapped in a safeCall function. A simple function that lives in a BaseViewModel:

Its purpose is to return data on successful calls or handle the error through parseError(e) (which could show a Toast, Snackbar, etc…).

However, in some cases, we may need to return more detailed information to the ViewModel or provide a better error handling UX experience, for instance, an error animation depending on the type. To do so, let’s rewind a bit, and focus again in our Repository (that’s why I’ve called it the fun part).

“Now the fun part, Repository.”

is Result.Success -> result.data
is Result.Error -> throw result.exception

Let’s update this piece of code with a more elegant approach. For that, let’s think of it as a “UseCase” response. A sealed class is a good candidate because it can represent states and hold data for a specific state:

Next we update our api call to:

Finally, our ViewModel:

This way the code is easier to read because it documents itself, and becomes easier to maintain and test.

Fun fact, Florina Muntenescu published an article Sealed with a class that talks about how to achieve this kind of behaviour. I was happy to notice that I was using her approach too.

Conclusion

With this refactor I’ve eliminated the need of an additional library, RxKotlin in this case, but most importantly - because one can argue that I could have used Flows and Channels too without importing RxKotlin -, we are using the right tools for the job:

An asynchronous programming pattern implemented with coroutines to perform “one-shot calls”.

Regarding Android App Architecture our Service and Repository communicate by a sealed class Result. Then, before returning it to ViewModel, if needed, the Repository maps it again into another sealed class representing a “UseCase”. This way a detailed response is returned and we also benefit from a cleaner code.

I hope you find this article useful, thanks for reading.

--

--

Software Engineer at BlissApplications 🇵🇹 • github.com/GuilhE • When I’m not coding I’m 🧗🏽or 🏄‍♂️