As you may know, Retrofit is an HTTP client which wraps the network layer of Android. It’s a great tool for developing Android apps with Java or Kotlin. However, it doesn’t provide any way to model responses in your code. This post will show how to use Plugable Sealed Result types to model response models in your code, and then use them with Retrofit.
The retrofit 2 simplexmlconverterfactory deprecated is a pluggable sealed API result type for modeling Retrofit responses.
Retrofit replies may be modelled using a pluggable sealed API result type.
Usage
Retrofit propagates errors via exceptions by default. This library uses Kotlin sealed types to properly represent these replies, providing a type-safe single point of return and eliminating the requirement for exception handling!
ApiResult is the fundamental type for this, with T denoting success and E denoting a potential error.
Success and Failure are the two sealed subclasses of ApiResult. Failure is written to E with no success type while success is typed to T with no error type. Failure, in turn, is represented by four different sealed subtypes: Failure. Failure, Failure, Failure, Failure, Failure, Failure, Failure, Failure, Failure, Failure, Failure, Failure ApiFailure, Failure.HttpFailure, and Failure.UnknownFailure are all examples of failures. This enables for easy processing of outcomes through sealed when branches in a consistent, non-exceptional flow.
when (val result = myApi.someEndpoint()) returns Success -> doSomethingWith(result.response) returns Failure -> when (result) returns NetworkFailure -> showError(result.error) returns HttpFailure -> showError(result.code) returns ApiFailure -> showError(result.error) returns Un
Typically, user code for this would just display a generic error message for a Failure case, however sealed subtypes allow for more precise error messaging or pluggability of error types.
Simply update the return type of your endpoint to typed ApiResult and add our call adapter and delegating converter factory.
@GET(“/”) interface TestApi ApiResultSuccessResponse, ErrorResponse> ApiResultSuccessResponse, ErrorResponse> ApiResultSuccessResponse, ErrorResponse> Api api = Retrofit.Builder(); val api = Retrofit.Builder(); val api = Retrofit.Builder() APIResultConverterFactory.addConverterFactory(ApiResultConverterFactory) APIResultCallAdapterFactory.addCallAdapterFactory(ApiResultCallAdapterFactory) build() is a function that allows you to construct something. create()
If you don’t have any custom error return types, set the error type to Nothing.
Error Bodies Decoding
Annotate your endpoint with @DecodeErrorBody: if you wish to decode error kinds in HttpFailures.
suspend fun getData(): ApiResultSuccessResponse, ErrorResponse> interface TestApi @DecodeErrorBody @GET(“/”) suspend fun getData(): ApiResultSuccessResponse, ErrorResponse>
A 4xx or 5xx answer will now attempt to decode its error body as ErrorResponse if one exists. You can get a @StatusCode annotation from annotations in a custom Retrofit Converter if you wish to contextually interpret the error content depending on the status code.
/ Create a converter factory of your own. override the amusement response Converter( type: Type, annotations: Array, retrofit: Retrofit ): *, *, *, *, *, *, *, *, *, *, *, *, *, *, *, * ? annotations = val (statusCode, nextAnnotations) return null for statusCode() when val errorType = (statusCode.value) 401 -> 401 -> 401 -> 401 -> 401 404 -> Unauthorized::class.java //… NotFound::class.java return MyCustomBodyConverter(errorDelegate) val errorDelegate = retrofit.nextResponseBodyConverter(this, errorType.toType(), nextAnnotations)
Error bodies having a content length of 0 will not be processed.
Plugability
Returning a polymorphic 200 response where the data must be dynamically processed is a frequent practice for certain APIs. Consider the following scenario:
“ok” is true, and “data” is…
This structure may be returned by the same API in an error event.
“ok” is set to false, and “error message” is set to “Please try again.”
This is difficult to represent with a single concrete type, but ApiResult makes it simple. In a custom Retrofit Converter, just throw an ApiException with the decoded error type, and it will be surfaced as a Failure. That error object has an ApiFailure type.
Suspend fun getData() with @GET(“/”): ApiResultSuccessResponse, ErrorResponse> / Create a converter factory of your own. ErrorConverterFactory is a class in the Converter.Factory package () responseBodyConverter( type: Type, annotations: Array, retrofit: Retrofit ): ConverterResponseBody, *>? override fun responseBodyConverter( type: Type, annotations: Array, retrofit: Retrofit ): ConverterResponseBody, *>? / This produces a @ResultType instance, which can be used to get the error type using toType() val (errorType, nextAnnotations) = annotations. return ResponseBodyConverter(errorType.toType()) if errorType() returns null ConverterResponseBody, *> class ResponseBodyConverter( private var errorType: Type ): override fun convert(value: ResponseBody): String override fun convert(value: ResponseBody): String override fun convert(value: ResponseBody var errorResponse =… raise ApiException(errorResponse) if (value.isErrorType()) else return SuccessResponse(…)
Installation
implementation(“com.slack.eithernet:eithernet:”) dependencies
[Sonatype’s snapshots repository][snap] has snapshots of the development version.
GitHub
https://github.com/slackhq/EitherNet
The kotlin sealed class response is a pluggable result type for modeling Retrofit responses. It allows the developer to make the response immutable, which makes it easier to test and reason about.
Related Tags
- retrofit xml response
- kotlin sealed class retrofit
- retrofit calladapter
- slack github
- retrofit response wrapper