Skip to content

Instantly share code, notes, and snippets.

@silviorp
Forked from iamhariomsharma/ApiExt.kt
Created August 3, 2024 22:01
Show Gist options
  • Save silviorp/2909b49ba63930ef80093796ea0e6ba4 to your computer and use it in GitHub Desktop.
Save silviorp/2909b49ba63930ef80093796ea0e6ba4 to your computer and use it in GitHub Desktop.
Retrofit Error & Response Handling Utility function
suspend fun <T : Any> executeApi(call: suspend () -> Response<T>): DataResult<T, DataError.Network> {
return try {
val response = call.invoke()
val body = response.body()
val errorBody = response.errorBody()
if (response.isSuccessful && body != null) {
DataResult.Success(body)
} else if (errorBody != null) {
val gson = Gson()
val errorResponse = gson.fromJson(String(errorBody.bytes()), ErrorResponse::class.java)
val messages = when (val message = errorResponse.error.message) {
is String -> message
is List<*> -> message.joinToString(separator = "\n") { it.toString() }
else -> "Unknown error"
}
if (messages.isNotEmpty()) {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.UNKNOWN, messages))
} else {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.UNKNOWN))
}
} else {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.UNKNOWN))
}
} catch (e: IOException) {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.NO_INTERNET))
} catch (e: SocketTimeoutException) {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.CONNECTION_TIMEOUT))
} catch (e: HttpException) {
when (e.code()) {
408 -> DataResult.Error(DataError.Network(DataError.Network.ErrorType.REQUEST_TIMEOUT))
413 -> DataResult.Error(DataError.Network(DataError.Network.ErrorType.PAYLOAD_TOO_LARGE))
401 -> DataResult.Error(DataError.Network(DataError.Network.ErrorType.SERVER_ERROR))
in 500..599 -> DataResult.Error(DataError.Network(DataError.Network.ErrorType.SERVER_ERROR))
else -> DataResult.Error(DataError.Network(DataError.Network.ErrorType.UNKNOWN))
}
} catch (e: JsonParseException) {
DataResult.Error(DataError.Network(DataError.Network.ErrorType.SERIALIZATION_ERROR))
} catch (e: Exception) {
DataResult.Error(
DataError.Network(
DataError.Network.ErrorType.UNKNOWN,
e.message.toString()
)
)
}
}
// Data Result
sealed interface Error
typealias RootError = Error
sealed interface DataResult<out D, out E: RootError> {
data class Success<out D, out E: RootError>(val data: D): DataResult<D, E>
data class Error<out D, out E: RootError>(val error: E): DataResult<D, E>
}
fun <D, E : RootError, R> DataResult<D, E>.map(map: (D) -> R): DataResult<R, E> {
return when (this) {
is DataResult.Success -> DataResult.Success(map(data))
is DataResult.Error -> DataResult.Error(error)
}
}
fun <T, E: Error> DataResult<T, E>.asEmptyDataResult(): EmptyResult<E> {
return map { }
}
typealias EmptyResult<E> = DataResult<Unit, E>
// DataError
sealed interface DataError: Error {
data class Network(val type: ErrorType, val message: String? = null) : DataError {
enum class ErrorType {
REQUEST_TIMEOUT,
TOO_MANY_REQUESTS,
NO_INTERNET,
CONNECTION_TIMEOUT,
PAYLOAD_TOO_LARGE,
SERVER_ERROR,
SERIALIZATION_ERROR,
UNKNOWN
}
}
enum class Local : DataError {
DISK_FULL,
PERMISSION_DENIED
}
}
// Usage in Repository:
override suspend fun getFolioDetail(id: String): DataResult<Folio?, DataError.Network> {
val response = executeApi { foliosApi.getFolioDetail(id) }
return response.map { baseResponse ->
baseResponse.data?.toFolio()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment