Skip to content

Integration with Back-End

One of the main feature of this codebase is integration with our Back-End's codebase. Using RxJava, this codebase transform API Response based on Back-end's response automatically either it's a positive or negative case by HTTP Error code.

Simple API Call

When defining ApiClient:

ExampleApiClient.kt
interface ExampleApiClient {

  @GET("sample")
  fun sampleAPI() : Single<Response<DevApiResponse<ActualResponseWeNeedItem>>>
}

The return type always follow by Single because the codebase using RXJava, Response for okhttp cause this is a response from API, and DevApiResponse to define what the actual data we need from the API. DevApiResponse look like this:

DevApiResponse.kt
class DevApiResponse<T> {

    @SerializedName("code")
    val code: Int? = null

    @SerializedName("success")
    val success: String? = null

    @SerializedName("message")
    val message: String? = null

    @SerializedName("data")
    val data: T? = null
}
SampleResponse.json
{
"code": 200,
"success" : "iya sukses",
"message": "iya berhasil kok",
"data": "ini generic yah teman-teman"
}

This is default response of positive case from Back-End's codebase, where the data variable contain the actual data needed. data variable is abstract, define it whenever the class is called. This helps to define what variable is needed and what is not in presentation layer.

Important

Use domain layer to only pass the data variable and configure the values based on presentation layer needs.

Negative Case of an API

When getting negative case from API, this codebase automatically use ApiError class which look like this:

ApiError.kt
data class ApiError(
@SerializedName("message", alternate = ["resultMessage", "error"])
var message: String = "",

    @SerializedName("resultCode", alternate = ["code"])
    var code: Int = 0,

    @SerializedName("success")
    var status: Boolean = false
)
SampleResponse.json
{
"code": 400,
"success" : "iya gagal",
"message": "iya gagal kok"
}

The code return the HTTP code based on the case, and the message contain information about the error from the API. By using lift(singleApiError()) in DataStore (See sample module for example), the codebase automagically help to return the correct error message as needed.

Additionally, in ViewModel it will look like this:

useCase.hitSomeApi().compose(singleScheduler)
    .subscribe({
         // Positive Case
        },{
         // Negative Case
        genericErrorHandler(it, result)
    }
).let(compositeDisposable::add)
See that we're using genericErrorHandler for the negative case. Because by using RXOperator, right from the domain layer we're actually already defining which exception it is from the API. This codebase actually cover some of those exception like SocketTimeOutException, IOException, and even JSONException.

Hint

Don't manually defining Internet connection listener before each call, or even reader for JSON when it comes back. The codebase already do that for you

For negative case right from the API, like 400 or 404 it handled too and we defined it as APIException. So when observing the live data in Activity/Fragment like this:

vm.observe(this) {
    when (it) {
        is Failure -> logError { it.message }
    }
}
Easily get the error message based on API response in ApiError from earlier.

Disclaimer

All of HTTP code is based on Back-End's codebase which look like this:

const ERROR = {
  'BAD_REQUEST': 400,
  'NOT_FOUND': 404,
  'INTERNAL_ERROR': 500,
  'CONFLICT': 409,
  'EXPECTATION_FAILED': 417,
  'FORBIDDEN': 403,
  'UNAUTHORIZED': 401,
  'SERVICE_UNAVAILABLE': 503,
  'GATEWAY_TIMEOUT': 504,
  'NOT_ACCEPTABLE' : 406
};

const SUCCESS = {
  'OK': 200,
  'CREATED': 201
};