JakeWharton/retrofit2-kotlin-coroutines-adapter

How to deal with exception

Closed this issue ยท 7 comments

How to deal with exception with Deferred await error, is there any solution for no try catch for this problem?
I fix this by extension function ,thanks

try/catch the await

@Test fun bodySuccess404() = runBlocking {
server.enqueue(MockResponse().setResponseCode(404))
val deferred = service.body()
try {
deferred.await()
fail()
} catch (e: HttpException) {
assertThat(e).hasMessageThat().isEqualTo("HTTP 404 Client Error")
}
}

thanks a lot!

try/catch the await

@Test fun bodySuccess404() = runBlocking {
server.enqueue(MockResponse().setResponseCode(404))
val deferred = service.body()
try {
deferred.await()
fail()
} catch (e: HttpException) {
assertThat(e).hasMessageThat().isEqualTo("HTTP 404 Client Error")
}
}

Is there any better way without putting all await() calls into try/catch? This solution is not preferred for apps with many network requests

I'm looking for a solution like the one @hossein-amini is looking for.
It's really inconvenient deal with try/catch for each call.

As I comment here #46 (comment)

i'm pretty new to kotlin so that surely can be improved (looking forward to it) but i've come up with:

suspend fun <A, T> toSuspendAwait(fn: (A) -> Deferred<Response<T>>) 
: suspend (A) -> Response<T>? = { a: A ->
try {
        fn(a).await()
    }
    catch (e: Exception) {
        Log.w("[APIAccess]","toSuspendAwait(): call thrown: ${e.message}")
        null
    }
}

and another one with no params for 'fn', then you call it like (squeezed to minimum):

interface APIAccess {
    @POST("/reservation")
    fun reservation(@Body body : ReservationCommand): Deferred<Response<ReservationResponse>>
}
private val mRetrofitService: Retrofit = makeRetrofitService()
private val mAPI: APIAccess = mRetrofitService.create(APIAccess::class.java)
val reservationResponse = toSuspendAwait(mAPI::reservation)(
                    ReservationCommand(
                        vehicle.externalId,
                        vehicle.provider,
                        providerStoredCredentials.getAuthToken().accessToken
                    )
                )
reservationResponse?.body()? < do something >

this way the reservationResponse will be null if request thrown exception.
I bet somebody can do better! :)

Using @rrozek code I manage to capture exception just once in my project.

open class BaseRepository{

 suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>): Resource<T>? {
   return safeApiResult(call, "Error")
}


private suspend fun <T: Any> safeApiResult(call: suspend ()-> Response<T>, errorMessage: String) : Resource<T>{
        return try {
            val response = call.invoke()
            if(response.isSuccessful) return Resource.Success(response.body()!!)
            else{

                val converterError = ErrorUtils.parseError(response)
                converterError.code = response.code()
                return Resource.Failure(converterError)
            }
        
        } catch (se3: SocketTimeoutException) {
            return Resource.Failure(Error("Error", 500))
        }catch (e: Throwable) {
            return Resource.Failure(Error("Error, no internet conection ", 500))
        }
    }
}

And then i use this BaseRository for every repository (network calls) :

class  LoginRepository (private val api : ApiServer): BaseRepository(){

    suspend fun login(license: String, pass: String) : Resource<AuthenticateResponse>?{
        return safeApiCall(call = { api.login(license, pass, true).await()}
)
}

Also read: https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777 --> good tutorial of how to use this library and create a baserepository