-
-
Save silviorp/2909b49ba63930ef80093796ea0e6ba4 to your computer and use it in GitHub Desktop.
Retrofit Error & Response Handling Utility function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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