networklib-kmp
1.2.1indexedEnhances HTTP client capabilities with caching, language, and user-agent plugins. Utilizes StateFlow for dynamic request handling, supporting cache control and management, and error recovery.
Enhances HTTP client capabilities with caching, language, and user-agent plugins. Utilizes StateFlow for dynamic request handling, supporting cache control and management, and error recovery.
Available on Maven Central:
implementation("ch.ubique.kmp:network:1.1.1")
You may find the current version and version history in the Releases list.
This networking library builds on Ktor.
On how to use Ktor, have a look at the following resources:
An application-level plugin for Ktor implementing a disk cache, supporting the major HTTP caching mechanisms as well as the custom cache rules specified by Ubique.
import ch.ubique.libs.ktor.plugins.Ubiquache
val client = HttpClient() {
install(Ubiquache)
}
On Android, the Ubiquache plugin needs to be initialized before installing it in the Ktor client, e.g. in Application.onCreate():
UbiquacheConfig.init(context)
If you need multiple independent caches, you can configure each plugin instance with a distinct name. Furthermore, you can set the maximum cache size in bytes:
val client = HttpClient() {
install(Ubiquache) {
name = "my-cache"
maxSize = 256 * 1024 * 1024 // 256 MB
}
}
The disk-level cache supports the cache control mechanisms as defined by following HTTP headers:
Cache-Control: no-cache – The response will not be loaded from cache and forces a network request.Cache-Control: no-store – The response will not be stored to cache, but may return a stored response from cache if it's valid.Cache-Control: only-if-cached – Prevent a network request. Fails with status code 504 if there is no valid cached response.A request is uniquely identified by the following attributes. If any of these values differ, the request is handled and cached separately.
By obtaining the plugin instance:
val ubiquache = httpClient.plugin(Ubiquache)
you can access basic cache information and perform cleanup operations:
ubiquache.clearCache() – Removes all cached responses.ubiquache.clearCache(url) – Removes the cached response for a specific URL.ubiquache.usedCacheSize() – Current cache size in bytes.ubiquache.maxCacheSize() – Maximum cache size in bytes.ktorStateFlow() creates a StateFlow that, if it has active observers, executes a request and automatically refreshes
according to the response cache headers, i.e. reloads if the response needs to be refreshed or is expired.
This requires the Ktor request to forward a Cache-Control header and return a Response with the desired result type:
val stateflow = ktorStateFlow<MyModel> { cacheControl ->
client.get(url) { cacheControl(cacheControl) }
}
The StateFlow holds the current state which is either Loading, Result containing the data, or Error with an exception and a retry function.
stateflow.collect { state ->
when (state) {
is RequestState.Loading -> { } // loading state
is RequestState.Result -> { state.data } // result state
is RequestState.Error -> { state.exception; state.retry() } // error state
}
}
In case of an error, the ktorStateFlow stops and has to be restarted manually, either with errorState.retry() or with stateflow.forceReload().
Example of a ktorStateFlow with a changing request parameter, e.g. a filter.
Setting the field exampleFilter automatically forces a reload with the new value:
var exampleFilter: String = "default"
set(value) {
field = value
stateflow.reload()
}
val stateflow = ktorStateFlow<summary> { cacheControl ->
client.get(url) {
url { parameter("filter", exampleFilter) }
cacheControl(cacheControl)
}
}
Or using a StateFlow as a value source instead, with flatMapLatestToKtorStateFlow():
val exampleFilter = MutableStateFlow("default")
val requestStateFlow = exampleFilter.flatMapLatestToKtorStateFlow { filter ->
ktorStateFlow<MyModel> { cacheControl ->
client.get(url) {
url { parameter("filter", filter) }
cacheControl(cacheControl)
}
}
}
Example of a method returning a new ktorStateFlow instance for different but constant parameter values:
fun stateflow(exampleId: String) = ktorStateFlow<MyModel> { cacheControl ->
client.get(url) {
url { parameter("exampleId", exampleId) }
cacheControl(cacheControl)
}
}
HTTP client plugin to add the Accept-Language HTTP header. Either with a fixed language code, or a system dependent language list.
val client = HttpClient() {
install(AcceptLanguage) {
language = "de" // static ...
languageProvider = { "de" } // ... or callback
}
}
HTTP client plugin to add the User-Agent HTTP header, containing basic system and app information.
val client = HttpClient() {
AppUserAgent()
}
Most features of this library can be implemented with test-driven development using unit tests with a mock webserver instance.
To test any changes locally in an app, you can either include the library via dependency substitution in an application project, or deploy a build to your local maven repository and include that from any application:
Define a unique custom version by setting the VERSION_NAME variable in the file.
Unit tests and coverage reports are run on the JVM target by default. See also workflows for Test and Coverage.
Create a Release,
setting the Tag to the desired version prefixed with a v.
Each release on Github will be deployed to Maven Central.
ch.ubique.kmpnetworkmajor.minor.revisionExpires: <date>X-Best-Before: <date> – and variants; synonymous with Expires.X-Next-Refresh: <date> – and variantsETag: <tag>, Last-Modified: <date>Cache-Control: max-age=<seconds>Cache-Control: no-cacheCache-Control: no-storegradle.propertiesDeploy the library artifact by running ./gradlew publishToMavenLocal
Reference the local maven repository in your application's build script:
repositories {
mavenLocal()
}
And apply the local library version:
dependencies {
implementation("ch.ubique.kmp:network:$yourLocalVersion")
}
Surfaced from shared tags and platforms — no rankings paid for.