Last active
September 28, 2025 19:19
-
-
Save ajinkya5130/cd3de0ae43a64cb47662fe742c9cb424 to your computer and use it in GitHub Desktop.
Generic HttpResponse converter
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
| package com.ajinkya.core.data.networking | |
| import com.ajinkya.core.domain.utils.DataError | |
| import com.ajinkya.core.domain.utils.Result | |
| import io.ktor.client.plugins.HttpRequestTimeoutException | |
| import io.ktor.client.statement.HttpResponse | |
| import io.ktor.network.sockets.SocketTimeoutException | |
| import io.ktor.util.network.UnresolvedAddressException | |
| import kotlinx.coroutines.ensureActive | |
| import kotlinx.serialization.SerializationException | |
| import java.net.ConnectException | |
| import java.net.UnknownHostException | |
| import kotlin.coroutines.coroutineContext | |
| actual suspend fun <T> platformSpecificSafeCall( | |
| execute: suspend () -> HttpResponse, | |
| handleResponse: suspend (HttpResponse) -> Result<T, DataError.Remote>, | |
| ): Result<T, DataError.Remote> { | |
| return try { | |
| val response = execute() | |
| handleResponse(response) | |
| }catch (e: UnknownHostException){ | |
| Result.Failure(DataError.Remote.NO_INTERNET_CONNECTION) | |
| }catch (e: UnresolvedAddressException){ | |
| Result.Failure(DataError.Remote.NO_INTERNET_CONNECTION) | |
| }catch (e: ConnectException){ | |
| Result.Failure(DataError.Remote.NO_INTERNET_CONNECTION) | |
| }catch (e: SocketTimeoutException){ | |
| Result.Failure(DataError.Remote.REQUEST_TIMEOUT) | |
| }catch (e: HttpRequestTimeoutException){ | |
| Result.Failure(DataError.Remote.REQUEST_TIMEOUT) | |
| }catch (e: SerializationException){ | |
| Result.Failure(DataError.Remote.SERIALIZATION_ERROR) | |
| }catch (e: Exception){ | |
| coroutineContext.ensureActive() | |
| Result.Failure(DataError.Remote.UNKNOWN) | |
| } | |
| } |
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
| import com.ajinkya.core.domain.utils.DataError | |
| import com.ajinkya.core.domain.utils.Result | |
| import io.ktor.client.engine.darwin.DarwinHttpRequestException | |
| import io.ktor.client.plugins.HttpRequestTimeoutException | |
| import io.ktor.client.statement.HttpResponse | |
| import io.ktor.network.sockets.SocketTimeoutException | |
| import io.ktor.util.network.UnresolvedAddressException | |
| import kotlinx.coroutines.ensureActive | |
| import kotlinx.serialization.SerializationException | |
| import platform.Foundation.NSURLErrorCallIsActive | |
| import platform.Foundation.NSURLErrorCannotFindHost | |
| import platform.Foundation.NSURLErrorDNSLookupFailed | |
| import platform.Foundation.NSURLErrorDataNotAllowed | |
| import platform.Foundation.NSURLErrorDomain | |
| import platform.Foundation.NSURLErrorInternationalRoamingOff | |
| import platform.Foundation.NSURLErrorNetworkConnectionLost | |
| import platform.Foundation.NSURLErrorNotConnectedToInternet | |
| import platform.Foundation.NSURLErrorResourceUnavailable | |
| import platform.Foundation.NSURLErrorTimedOut | |
| import platform.Foundation.NSURLErrorUnknown | |
| import kotlin.coroutines.coroutineContext | |
| actual suspend fun <T> platformSpecificSafeCall( | |
| execute: suspend () -> HttpResponse, | |
| handleResponse: suspend (HttpResponse) -> Result<T, DataError.Remote>, | |
| ): Result<T, DataError.Remote> { | |
| return try { | |
| val response = execute() | |
| handleResponse(response) | |
| }catch (e: DarwinHttpRequestException){ | |
| handleDarwinException(e) | |
| }catch (e: UnresolvedAddressException){ | |
| Result.Failure(DataError.Remote.NO_INTERNET_CONNECTION) | |
| }catch (e: SocketTimeoutException){ | |
| Result.Failure(DataError.Remote.REQUEST_TIMEOUT) | |
| }catch (e: HttpRequestTimeoutException){ | |
| Result.Failure(DataError.Remote.REQUEST_TIMEOUT) | |
| }catch (e: SerializationException){ | |
| Result.Failure(DataError.Remote.SERIALIZATION_ERROR) | |
| }catch (e: Exception){ | |
| coroutineContext.ensureActive() | |
| Result.Failure(DataError.Remote.UNKNOWN) | |
| } | |
| } | |
| private fun handleDarwinException(e:DarwinHttpRequestException):Result<Nothing, DataError.Remote>{ | |
| val nsError = e.origin | |
| return if (nsError.domain == NSURLErrorDomain){ | |
| when(nsError.code){ | |
| NSURLErrorNotConnectedToInternet, | |
| NSURLErrorNetworkConnectionLost, | |
| NSURLErrorCannotFindHost, | |
| NSURLErrorDNSLookupFailed, | |
| NSURLErrorResourceUnavailable, | |
| NSURLErrorInternationalRoamingOff, | |
| NSURLErrorCallIsActive, | |
| NSURLErrorDataNotAllowed -> Result.Failure(DataError.Remote.NO_INTERNET_CONNECTION) | |
| NSURLErrorTimedOut -> Result.Failure(DataError.Remote.TIMEOUT) | |
| else -> { | |
| Result.Failure(DataError.Remote.UNKNOWN) | |
| } | |
| } | |
| }else Result.Failure(DataError.Remote.UNKNOWN) | |
| } |
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
| import com.ajinkya.core.data.networking.utils.UrlConstants | |
| import com.ajinkya.core.domain.utils.DataError | |
| import com.ajinkya.core.domain.utils.Result | |
| import io.ktor.client.call.NoTransformationFoundException | |
| import io.ktor.client.call.body | |
| import io.ktor.client.statement.HttpResponse | |
| expect suspend fun <T> platformSpecificSafeCall( | |
| execute: suspend () -> HttpResponse, | |
| handleResponse: suspend (HttpResponse) -> Result<T, DataError.Remote>, | |
| ): Result<T, DataError.Remote> | |
| suspend inline fun <reified T> safeCall(noinline execute: suspend () -> HttpResponse): Result<T, DataError.Remote> { | |
| return platformSpecificSafeCall( | |
| execute = execute | |
| ) { rep: HttpResponse -> | |
| responseToResult(rep) | |
| } | |
| } | |
| suspend inline fun <reified Request, reified Response: Any> HttpClient.post( | |
| route: String, | |
| queryParams: Map<String, String> = emptyMap(), | |
| body: Request? = null, | |
| crossinline builder: HttpRequestBuilder.() -> Unit = {} | |
| ): Result<Response, DataError.Remote> { | |
| return safeCall { | |
| post{ | |
| url(route) | |
| queryParams.forEach { (key, value) -> | |
| parameter(key,value) | |
| } | |
| setBody(body) | |
| builder() | |
| } | |
| } | |
| } | |
| suspend inline fun <reified Request, reified Response: Any> HttpClient.get( | |
| route: String, | |
| queryParams: Map<String, String> = emptyMap(), | |
| body: Request? = null, | |
| crossinline builder: HttpRequestBuilder.() -> Unit = {} | |
| ): Result<Response, DataError.Remote> { | |
| return safeCall { | |
| get{ | |
| url(route) | |
| queryParams.forEach { (key, value) -> | |
| parameter(key,value) | |
| } | |
| setBody(body) | |
| builder() | |
| } | |
| } | |
| } | |
| suspend inline fun <reified Request, reified Response: Any> HttpClient.delete( | |
| route: String, | |
| queryParams: Map<String, String> = emptyMap(), | |
| body: Request? = null, | |
| crossinline builder: HttpRequestBuilder.() -> Unit = {} | |
| ): Result<Response, DataError.Remote> { | |
| return safeCall { | |
| delete{ | |
| url(route) | |
| queryParams.forEach { (key, value) -> | |
| parameter(key,value) | |
| } | |
| setBody(body) | |
| builder() | |
| } | |
| } | |
| } | |
| suspend inline fun <reified Request, reified Response: Any> HttpClient.put( | |
| route: String, | |
| queryParams: Map<String, String> = emptyMap(), | |
| body: Request? = null, | |
| crossinline builder: HttpRequestBuilder.() -> Unit = {} | |
| ): Result<Response, DataError.Remote> { | |
| return safeCall { | |
| put{ | |
| url(route) | |
| queryParams.forEach { (key, value) -> | |
| parameter(key,value) | |
| } | |
| setBody(body) | |
| builder() | |
| } | |
| } | |
| } | |
| //DataError.Remote.SERIALIZATION_ERROR --- Check DataError.kt file | |
| suspend inline fun <reified T> responseToResult(response: HttpResponse): Result<T, DataError.Remote> { | |
| return when (response.status.value) { | |
| in 200..299 -> try { | |
| Result.Success(response.body<T>()) | |
| } catch (e: NoTransformationFoundException) { | |
| Result.Failure(DataError.Remote.SERIALIZATION_ERROR) | |
| } | |
| 400 -> Result.Failure(DataError.Remote.BAD_REQUEST) | |
| 401 -> Result.Failure(DataError.Remote.UNAUTHORIZED) | |
| 403 -> Result.Failure(DataError.Remote.FORBIDDEN) | |
| 404 -> Result.Failure(DataError.Remote.NOT_FOUND) | |
| 408 -> Result.Failure(DataError.Remote.REQUEST_TIMEOUT) | |
| 409 -> Result.Failure(DataError.Remote.CONFLICT) | |
| 413 -> Result.Failure(DataError.Remote.PAYLOAD_TOO_LARGE) | |
| 429 -> Result.Failure(DataError.Remote.TOO_MANY_REQUESTS) | |
| 500 -> Result.Failure(DataError.Remote.SERVER_ERROR) | |
| 503 -> Result.Failure(DataError.Remote.SERVICE_UNAVAILABLE) | |
| else -> Result.Failure(DataError.Remote.UNKNOWN) | |
| } | |
| } | |
| fun constructRoute(route: String): String { | |
| //BASE_URL ---> "https://abc.com/api" | |
| return when { | |
| route.contains(UrlConstants.BASE_URL) -> route | |
| route.startsWith("/") -> UrlConstants.BASE_URL + route | |
| else -> UrlConstants.BASE_URL + "/" + route | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment