Skip to content

Instantly share code, notes, and snippets.

@ajinkya5130
Last active September 28, 2025 19:19
Show Gist options
  • Select an option

  • Save ajinkya5130/cd3de0ae43a64cb47662fe742c9cb424 to your computer and use it in GitHub Desktop.

Select an option

Save ajinkya5130/cd3de0ae43a64cb47662fe742c9cb424 to your computer and use it in GitHub Desktop.
Generic HttpResponse converter
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)
}
}
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)
}
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