Skip to content

AndroidPoet/KtorBoost

Repository files navigation

KtorBoost



License API Build Status Android Weekly Profile


πŸš€ Simplifying Ktor for Easier Development. Ktor Boost streamlines HTTP requests in Ktor by offering functions that neatly package results in the Kotlin's Result class. It makes handling successes and errors clearer, simplifying error control in Ktor apps


Download

Maven Central

Without this line, you will receive an error response in the 'success' field.

val client = HttpClient() {
    expectSuccess = true
}

Gradle

Add the dependency below to your module's build.gradle file:

sourceSets {
    val commonMain by getting {
        dependencies {
            implementation("io.github.androidpoet:ktor-boost:$version")
    
        }
    }
}

Usage

// normal get,post,put...methods will be replaced by getResult,postResult,putResult...

val result = httpClient.getResult<String>("sample_get_url")

val result = httpClient.postResult<String>("sample_post_url")

val result = httpClient.putResult<String>("sample_put_url")

val result = httpClient.deleteResult<String>("sample_delete_url")

val result = httpClient.patchResult<String>("sample_patch_url")

val result = httpClient.headResult<String>("sample_head_url")

val result = httpClient.optionsResult<String>("sample_options_url")


// for async and await methods use these

val result = httpClient.getResultAsync<String>("sample_get_url")

val result = httpClient.postResultAsync<String>("sample_post_url")

val result = httpClient.putResultAsync<String>("sample_put_url")

val result = httpClient.deleteResultAsync<String>("sample_delete_url")

val result = httpClient.patchResultAsync<String>("sample_patch_url")

val result = httpClient.headResultAsync<String>("sample_head_url")

val result = httpClient.optionsResultAsync<String>("sample_options_url")


val deferredResult = httpClient.getResultAsync<String>("sample_get_url")
val result = deferredResult.await()

Without KtorBoost

Initially, when fetching data from APIs, the code directly gets the response's content, leading to repetitive use of the 'body()' method:

// api service claas function 
suspend fun getUserNames(): List<String> {
    return httpClient.get("trendingMovies").body()
}


//for async oprations

suspend fun getAsyncUserNames(): Deferred<List<String>> {
    return coroutineScope { async { httpClient.get("trendingMovies").body<String>() } }
}


Handling errors involves enclosing each API call within separate try-catch blocks, causing code duplication:

// viewModel

viewModelScope.launch {
    try {
        val data = apiService.getUserNames()
        // handle data
    } catch (e: Throwable) {
        // handle error 
    }
}

// Async Await

viewModelScope.launch {
    try {
        val deferredData = apiService.getAsyncUserNames()
        deferredResult.await()
        // handle data
    } catch (e: Throwable) {
        // handle error 
    }
}

With KtorBoost

With improvements, the API calls are now focused solely on returning values:

// api service claas function 
suspend fun getUserNamesResult() = httpClient.getResult<List<String>>("trendingMovies")


//for async oprations

suspend fun getAsyncUserNames(): = httpClient.getResultAsync<List<String>>("trendingMovies")

This refined approach in error and response handling simplifies the code:

// viewModel

viewModelScope.launch {
    val result = apiService.getUserNames()
    result.onSuccess { data ->
        // handle data
    }.onFailure { error ->
        // handle error 
    }
}


// Async Await

viewModelScope.launch {
    val deferredResult = apiService.getAsyncUserNames()
    val result = deferredResult.await()

    result.onSuccess { data ->
        // handle data
    }.onFailure { error ->
        // handle error 
    }

}

Different variations and use cases

The solution offers multiple variations in response handling, enabling adaptability based on team preferences and project needs:

//If you prefer executing the response function as a suspend function, useful for nested suspend
//function usage on success or error:

viewModelScope.launch {
    result.onSuccessSuspend { data ->
        // handle data
    }.onFailureSuspend { error ->
        // handle error
    }
}
// For cases solely interested in success response, using getOrNull() function and handling errors manually:

viewModelScope.launch {
    if (result.isSuccess) {
        val data = result.getOrNull()
        // handle data
    } else {
        // handle error
    }
}
// Folding the response, a concise approach for clean and readable code:

viewModelScope.launch {
    result.fold(onSuccess = { data ->
        // handle data
    }, onFailure = { error ->
        // handle error
    })
}
// Fold also supports suspend version:

viewModelScope.launch {
    result.foldSuspend(
        onSuccess = { data ->
            // handle data
        }, onFailure = { error ->
            // handle error
        })

}

These diverse approaches in response handling empower you to select the most suitable method, whether for executing suspend functions, specifically handling success responses, or streamlining code for clearer readability.

Find this repository useful? ❀️

Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🀩

License

Copyright 2023 AndroidPoet (Ranbir Singh)

    Licensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.