Search code examples
androidkotlingenericsexceptionretrofit2

How can I extract the (successful) response body from a custom retrofit exception handler


I followed this guide and found this question for handling network exceptions with Retrofit. While they both are very useful, none of them explain how to extract the body of a successful request. Here is a snippet of code that might better explain what I'm trying to accomplish:

The sealed class remains the same from both examples...

// exception handling:
sealed class NetworkResponse<out T : Any, out U : Any> {

    data class Success<T : Any>(val body: T) : NetworkResponse<T, Nothing>()

    data class ApiError<U : Any>(val body: U, val code: Int) : NetworkResponse<Nothing, U>()

    data class NetworkError(val error: IOException) : NetworkResponse<Nothing, Nothing>()

    data class UnknownError(val error: Throwable) : NetworkResponse<Nothing, Nothing>()
}

but extracting the body on a successful call is unclear to me. Here is my attempt:

val oAuthRetrofit: Retrofit = Retrofit.Builder()
    .baseUrl(OAUTH_BASE_URL)
    .addConverterFactory(Json.asConverterFactory(contentType))
    .build()
val salesforceOAuthAPI: SalesforceInterface =
    oAuthRetrofit.create(SalesforceInterface::class.java)

val oAuthAccessTokenResponse: NetworkResponse<OAuthAccessTokenResponse, Error> =
    salesforceOAuthAPI.getOAuthAccessToken(
        GRANT_TYPE,
        BuildConfig.SALESFORCE_CLIENT_ID,
        BuildConfig.SALESFORCE_CLIENT_SECRET,
        BuildConfig.SALESFORCE_USERNAME,
        BuildConfig.SALESFORCE_PASSWORD + BuildConfig.SALESFORCE_SECURITY_TOKEN
    )

val body = oAuthAccessTokenResponse.body  // Unresolved reference
// Use body()

How can I extract the body of a successful request?


Solution

  • In the medium post you have access to the Source Code there you can check what you missing.

    Answering your question

    How can I extract the body of a successful request?

    You can simply do this :

    val response1 = salesforceOAuthAPI.getWhatever()
    when (response1) {
          is NetworkResponse.Success -> Log.d(TAG, "Success ${response1.body.name}")
          is NetworkResponse.ApiError -> Log.d(TAG, "ApiError ${response1.body.message}")
          is NetworkResponse.NetworkError -> Log.d(TAG, "NetworkError")
          is NetworkResponse.UnknownError -> Log.d(TAG, "UnknownError")
    }
    

    And in the NetworkResponse.Success you have the data you want, but since you are using a sealed class to detect the error and the data, you don't know what is inside that object until you do a when.

    By the way, don't know if you followed all the post but you missing the

    .addCallAdapterFactory(NetworkResponseAdapterFactory())
    

    In the Retrofit.Builder() otherwise it won't do the magic to encapsulate everything to a NetworkResponse