This is small library that provides Kotlin Coroutines suspending extension Call.await()
for Retrofit 2
Based on kotlinx.coroutines implementation
Download the JAR:
Gradle:
compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.5.0'
Maven:
<dependency>
<groupId>ru.gildor.coroutines</groupId>
<artifactId>kotlin-coroutines-retrofit</artifactId>
<version>0.5.0</version>
</dependency>
There are three suspending extensions:
Common await API that returns result or throw exception
fun Call<T>.await(): T
In case of HTTP error or invocation exception await()
throws exception
// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>) = runBlocking {
try {
// Wait (suspend) for result
val user: User = api.getUser("username").await()
// Now we can work with result object
println("User ${user.name} loaded")
} catch (e: HttpException) {
// Catch http errors
println("exception${e.code()}", e)
} catch (e: Throwable) {
// All other exceptions (non-http)
println("Something broken", e)
}
}
Common await API that returns response or throw exception
fun Call<T>.awaitResponse(): Response<T>
In case of invocation exception awaitResponse()
throws exception
// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>) = runBlocking {
try {
// Wait (suspend) for response
val response: Response<User> = api.getUser("username").awaitResponse()
if (response.isSuccessful()) {
// Now we can work with response object
println("User ${response.body().name} loaded")
}
} catch (e: Throwable) {
// All other exceptions (non-http)
println("Something broken", e)
}
}
API based on sealed class Result
:
fun Call<T>.awaitResult(): Result<T>
fun main(args: Array<String>) = runBlocking {
// Wait (suspend) for Result
val result: Result<User> = api.getUser("username").awaitResult()
// Check result type
when (result) {
//Successful HTTP result
is Result.Ok -> saveToDb(result.value)
// Any HTTP error
is Result.Error -> log("HTTP error with code ${result.error.code()}", result.error)
// Exception while request invocation
is Result.Exception -> log("Something broken", e)
}
}
Also, Result
has a few handy extension functions that allow to avoid when
block matching:
fun main(args: Array<String>) = runBlocking {
val result: User = api.getUser("username").awaitResult()
//Return value for success or null for any http error or exception
result.getOrNull()
//Return result or default value
result.getOrDefault(User("empty-user"))
//Return value or throw exception (HttpException or original exception)
result.getOrThrow()
//Also supports custom exceptions to override original ones
result.getOrThrow(IlleagalStateException("User request failed"))
}
All Result
classes also implemented one or both interfaces: ResponseResult
and ErrorResult
You can use them for access to shared properties of different classes from Result
fun main(args: Array<String>) = runBlocking {
val result: User = api.getUser("username").awaitResult()
//Result.Ok and Result.Error both implement ResponseResult
if (result is ResponseResult) {
//And after smart cast you now have an access to okhttp3 Response property of result
println("Result ${result.response.code()}: ${result.response.message()}")
}
//Result.Error and Result.Exception implement ErrorResult
if (result is ErrorResult) {
// Here yoy have an access to `exception` property of result
throw result.exception
}
}