Home > Software engineering >  Retrofit wrap response in a generic class internally
Retrofit wrap response in a generic class internally

Time:10-23

I have a sealed class like below,

sealed class ApiResult<T>(
    val data: T? = null,
    val errors: List<Error>? = null
) {
    class Success<T>(data: T?) : ApiResult<T>(data)
    class Failure<T>(
        errors: List<Error>,
        data: T? = null
    ) : ApiResult<T>(data, errors)
}

And this is my Retrofit API interface,

interface Api {
    @POST("login")
    suspend fun login(@Body loginRequestDto: LoginRequestDto): ApiResult<LoginResponseDto>
}

What I want to achieve is, internally wrap the LoginResponseDto in BaseResponseDto which has the success, error, and data fields. Then put them in the ApiResult class.

data class BaseResponseDto<T>(
    val success: Boolean,
    val errors: List<Int>,
    val data: T?
)

In this case, LoginResponseDto is my data class. So, Retrofit should internally wrap the LoginResponseDto in BaseResponseDto then I will create the ApiResult response in my custom call adapter. How can I tell Retrofit to internally wrap this? I don't want to include the BaseResponseDto in the API interface every time.

CodePudding user response:

After spending the whole day, I could achieve it. I had to create a custom converter. Here it is,

class ApiConverter private constructor(
    private val gson: Gson
) : Converter.Factory() {
    override fun responseBodyConverter(
        type: Type,
        annotations: Array<out Annotation>,
        retrofit: Retrofit
    ): Converter<ResponseBody, *> {
        val baseResponseType = TypeToken.get(BaseResponseDto::class.java).type

        val finalType = TypeToken.getParameterized(baseResponseType, type)

        return Converter<ResponseBody, Any> { value ->
            val baseResponse: BaseResponseDto<*> =
                gson.fromJson(value.charStream(), finalType.type)
            baseResponse
        }
    }

    companion object {
        fun create(gson: Gson) = ApiConverter(gson)
    }
}

Now I don't have to specify the BaseResponseDto every time in the API interface for retrofit.

CodePudding user response:

Great question!

If you want to implement your own solution you would need a custom Retrofit CallAdapter.

My recommendation would be Sandwich an Open source Library which does the exact same thing.

If you want to implement your own soultion here's one way

Modeling Retrofit Responses With Sealed Classes and Coroutines

  • Related