Created
August 3, 2024 16:13
-
-
Save iamhariomsharma/3223b2b839261f8b86eff7d45f9a9235 to your computer and use it in GitHub Desktop.
Retrofit Error & Response Handling Utility function
This file contains 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