So the first step that we need to take here is configure our retrofit setup to allow the return of what are known as a Deferred. This Deferred type is a non-blocking future that can be cancelled if required, this essentially represents a coroutine Job which contains a value for the corresponding work. Using a Deferred type allows us to incorporate the same ideas as a Job, with the addition of being able to retrieve extra states such as the success and failure of the Job — which makes it perfect for network requests.

If you’re using retrofit and RxJava, chances are that you’re using an RxJava Call Adapter Factory — luckily for us there is an equivalent for coroutines:

We can then add this call adapter to our Retrofit Builder process and then we will be able to implement our Retrofit interface in pretty much the same way that we have been with RxJava.

private fun makeService(okHttpClient: OkHttpClient): MyService {

val retrofit = Retrofit.Builder()

.baseUrl("some_api")

.client(okHttpClient)

.addCallAdapterFactory(CoroutineCallAdapterFactory())

.build()

return retrofit.create(MyService::class.java)

}

So when it comes to the MyService interface that is used above, we can now change that retrofit interface to now return us Deferrable types instead of Observable types. So where we may have previously had:

@GET("some_endpoint")

fun getData(): Observable<List<MyData>>

We can simply change this to:

@GET("some_endpoint")

fun getData(): Deferred<List<MyData>>

At this point, any time we call this getData() function we will be returned a Deferred instance — this is the Job for the network request that is taking place. When we previously may have called this function with RxJava we might have had something like this within our calling class:

override fun getData(): Observable<List<MyData>> {

return myService.getData()

.map { result ->

result.map { myDataMapper.mapFromRemote(it) }

}

}

In this RxJava flow we are calling our service function, then performing a map operation from the RxJava API, followed by mapping our data classes from the result of our our request to some format that is used by our UI layer. This changes slightly when we make the switch over to a coroutines implementation — to begin with, our function needs to become a suspend function. This is because we are calling going to be making a suspending operation within the function body, and in-order to do this the calling function must also be a suspending function. A suspending function is non-blocking and can be managed once triggered — such as being started, paused, resumed and cancelled.

override suspend fun getData(): List<MyData> {

...

}

Next we need to call our service function, at a first glance this looks like we are carrying out the same thing but remember we are now receiving an instance of a Deferred rather than an Observable:

override suspend fun getData(): List<MyData> {

val result = myService.getData()

...

}

Because of this change, we can no longer use our chained map operation from the RxJava API — and in fact at this point, we don’t even have our data available as we only have the Deferred instance and not the value that represents it. What we need to do here is use the await() function to wait for the result of our request and then continue with our function body once a value has been received:

override suspend fun getData(): List<MyData> {

val result = myService.getData().await()

...

}

At this point our request would have been completed and we will have our data available for use. Because of this we can now perform our mapping operation that we were carrying out previously and return the result from our function: