Safe & Simple REST API call in Android with Kotlin coroutines + Lambda

Dariush Fat'hi
5 min readMar 4, 2020

Just for keeping it short and simply assume we’re going to fetch a list of users of special topic form REST API. it looks like this in Retrofit:

interface UsersService {    @GET("getAllUsers/{topic}")
suspend fun getAllUsers(@Path("topic") String topic): List<User>
}

How does the repository look like?

it’s so simple and straightforward and there is no need to explaining it.

class ChatRepository @Inject constructor(
private val usersService: UsersService
) {
suspend fun getAllUsers(topic: String) = safeApiCall {
usersService.getAllUsers(topic)
}
}

What’s safeApiCall? it’s where the magic happens:

suspend inline fun <T> safeApiCall(
crossinline body: suspend () -> T
): ResponseResult<T> {
return try {
// blocking block
val users = withContext(Dispatchers.IO) {
body()
}
ResponseResult.Success(users)
} catch (e: Exception) {
ResponseResult.Failure(e)
}
}
are you kissing me?! by https://unsplash.com/photos/fXVx1opWGxM

it’s complicated? MAYBE!. I wanna explain it, as I can.

  • safeApiCall is suspending function: because we are calling another suspend function, and kotlin force us to mark it as a suspended. and actually, we are going to block current thread by calling this block :
withContext(Dispatchers.IO) {
body() // it's suspend function too
}
  • inline & crossline: these are simple keyword in kotlin. you can read about them here
  • lamdas also can be marked as a suspend function. we call it body, and it’s going to replace with a call to the repository suspend function(it’s the reason that we marked it as a suspend)
  • Dispatchers.IO: telling the kotlin coroutine to run the block body on IO Thread. it’s going to block the thread until the body completely executed

In the end, if no error happens, we return a ResponseResult.Success() otherwise, we have an error and must return ResponseResult.Failure()

  • ResponseResult is a Sealed class. you can read kotlin official documentation if you’re not familiar with sealed classes.
sealed class ResponseResult<out H> {
data class Success<out T>(val data: T)
: ResponseResult<T>()
data class Failure(val error: Throwable)
: ResponseResult<Nothing>()
object Pending : ResponseResult<Nothing>()
object Complete : ResponseResult<Nothing>()
}

Let’s look at how Antonio Leiva defines sealed classes:

A sealed class allows you to represent constrained hierarchies in which an object can only be of one of the given types.

That is, we have a class with a specific number of subclasses. What we get in the end is a concept very similar to an enum. The difference is that in the enum we only have one object per type, while in the sealed classes we can have several objects of the same class.

This difference will allow objects from a sealed class to keep state. This will bring us some advantages that we’ll see in a moment, and also opens the doors to some functional ideas.

A sealed class allows you to represent constrained hierarchies in which an object can only be of one of the given types.

Connect all these wired things!

At this point, we have fetched all users. now it’s time to connect them to the ViewModel :

class UserViewModel @Inject constructor(
private val usersRepository: UsersRepository
) : ViewModel() {

fun getUsers(topic: String) = liveResponse {
chatRepository.getAllUsers(topic)
}
}

OK. another magic here. what’s liveResponse? yes, it is another wired inlined ugly (it’s not my opinion!) function that gets a lambda function as a parameter. look at how it’s BEAUTIFUL:

inline fun <T> liveResponse(crossinline body: suspend () ->  ResponseResult<T>) =
liveData(Dispatchers.IO) {
emit(ResponseResult.Pending)
val result = body()
emit(result)
emit(ResponseResult.Complete)
}

liveData came with the AndroidX lifecycle package, and it’s wrapping a suspend function(in our example body lambda function) into a LiveData. in this way, we can notify UI from the Changes that happen in the coroutines.

liveData block is a bridge between UI and coroutines, it’s transform coroutines side events to into UI side event by a mediator liveData

If you want to use liveData block you have to add these dependencies to your app build.gradle file :

  • For use androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01 .
  • For liveData, use androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01 .

Now How to Notify Activity(Fragment)?

I assume you’re familiar with viewModels instance creation and how to inject dependencies into them using Dagger(or Koin). if you’re not, just looking at the great samples below:

In the UsersFragment we do like this:

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
chatViewModel.getUsers("programmer").observe(viewLifecycleOwner,
Observer {
when(it){
is ResponseResult.Success-> {
val users = it.data
// notify recyclerView
}
is ResponseResult.Failure -> {
val error = it.error
// some error happened
}
is ResponseResult.Pending-> {
// start | in progress
// show Loading...
}
is ResponseResult.Complete-> {
// call ended | completed
// hide Loading...
}
}
})
}

It was All of it!.

It’s logical if you think it’s look very complicated and not easy or simple at all!. I will prove you’re in the wrong position. just notice that we write these ugly complicated wired things once but we use them a lot.

let’s join all of the code lines that we need to call a REST API and finally deliver them to the UI :

1. retrofit service  
@GET("getAllUsers/{topic}")
suspend fun getAllUsers(@Path("topic") String topic): List<User>
2. repository
suspend fun getAllUsers(topic: String) = safeApiCall {
usersService.getAllUsers(topic)
}
3. viewModel
fun getUsers(topic: String) = liveResponse {
chatRepository.getAllUsers(topic)
}
4. UI
chatViewModel.getUsers("programmer").observe(viewLifecycleOwner,
Observer {
when(it){
is ResponseResult.Success-> {
val users = it.data
// notify recyclerView
}
is ResponseResult.Failure -> {
val error = it.error
// some error happened
}
is ResponseResult.Pending-> {
// start | in progress
// show Loading...
}
is ResponseResult.Complete-> {
// call ended | completed
// hide Loading...
}
}
})

It’s really very simple | beautiful | structured | well-defined | nice | cute | separated | organized | sorted | straightforward | explicit | clean | testable | flexible.

what’s your opinion? let me know it. just destroy that clap button and write me a comment at the comment section below.

My English is not very good. apologize for that.

Thank you all.

hope you the best.

Notice

This article is going to be updated by these titles:

  • How to disallow live data block to be called automatically
  • How to manually call live data block
  • Simplifying UI Side Codes
  • Using Transformation(Map|SiwtchMap) with LiveData block
  • More lambdas

--

--