
Persistent HTTP caching for Ktor HttpClient with disk-backed storage, configurable TTL and max size, LRU eviction, Vary-header aware variants, and optional custom cache-directory provider.
A Kotlin Multiplatform library that adds persistent HTTP caching to Ktor HttpClient. Responses are stored on disk using Okio, with configurable size limits, TTL, and platform-appropriate cache directories.
Vary headers so different variants (e.g. by
Accept-Language) are cached separately.| Platform | Cache directory |
|---|---|
| Android | Application cache dir (context.cacheDir) |
| iOS | App caches directory (NSCachesDirectory in the sandbox) |
| JVM | java.io.tmpdir/ktor-cache |
ktor-client-core 3.4.0+) and an engine (CIO, OkHttp, etc.) for your
targetsAdd the dependency to your shared or platform source sets.
Kotlin DSL (Gradle):
repositories {
mavenCentral()
// For snapshots:
// maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
commonMain.dependencies {
implementation("io.github.santimattius:ktor-persistent-cache:1.0.0-SNAPSHOT")
}
// Also add a Ktor engine for each target, e.g.:
// implementation("io.ktor:ktor-client-okhttp") // Android
// implementation("io.ktor:ktor-client-cio") // iOS / JVM
}Version catalog (e.g. libs.versions.toml):
[versions]
ktorPersistentCache = "1.0.0-SNAPSHOT"
[libraries]
ktor-persistent-cache = { group = "io.github.santimattius", name = "ktor-persistent-cache", version.ref = "ktorPersistentCache" }Then:
implementation(libs.ktor.persistent.cache)The library needs the application context to resolve the cache directory. The recommended way is App Startup:
Merge the library’s manifest
The shared (or Android) module that depends on ktor-persistent-cache should merge the
library’s AndroidManifest so that the App Startup InitializationProvider and
ContextInitializer are registered.
No extra code
If the manifest is merged, ContextInitializer runs at app startup and injects the application
context. getCacheDirectoryProvider()
will then use it automatically.
If you don’t use the library’s manifest (e.g. you use a different DI or startup path), you must call once at app startup with the application context (not an Activity context):
// e.g. in Application.onCreate()
injectContext(applicationContext)injectContext is provided by the library package
io.github.santimattius.persistent.cache.startup.
No setup. The library uses the default app caches directory.
No setup. The library uses a subdirectory of the JVM temp directory.
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.github.santimattius.persistent.cache.*
val client = HttpClient(CIO) {
installPersistentCache(
CacheConfig(
enabled = true,
cacheDirectory = "http_cache",
maxCacheSize = 10L * 1024 * 1024, // 10 MB
cacheTtl = 60 * 60 * 1000, // 1 hour
isShared = true,
isPublic = false
)
)
}val response: String = client.get("https://example.com/api/data").body()CacheConfig supports:
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
Boolean |
false |
Whether the HTTP cache is enabled. |
cacheDirectory |
String |
"http_cache" |
Name of the cache directory under the platform cache root. |
maxCacheSize |
Long |
10 MB | Maximum cache size in bytes. LRU eviction when exceeded. Use 0 for no limit. |
cacheTtl |
Long |
1 hour | Time-to-live for entries in milliseconds. |
isShared |
Boolean |
true |
Whether the cache is shared across requests (Ktor HttpCache behavior). |
isPublic |
Boolean |
false |
When true, cached responses are treated as public (shareable across users); when false, they are private to the client. |
Convenience constructor:
CacheConfig(enabled = true, cacheDirectory = "my_cache")
// Other properties use defaults.To control where the cache is stored (e.g. a custom folder or test directory), implement CacheDirectoryProvider and pass it to installPersistentCache:
val customProvider = object : CacheDirectoryProvider {
override val cacheDirectory: Path get() = FileSystem.SYSTEM.toPath("/custom/cache/dir")
}
HttpClient(CIO) {
installPersistentCache(
config = CacheConfig(enabled = true, cacheDirectory = "http_cache"),
cacheDirectoryProvider = customProvider
)
}Default behavior (no custom provider): getCacheDirectoryProvider() returns the platform implementation (Android app cache dir, iOS caches dir, or JVM temp dir).
Build:
./gradlew :shared:compileKotlinIosSimulatorArm64 :shared:compileAndroidMain
# or
./gradlew :shared:assembleRun tests:
./gradlew testPublish to local Maven:
./gradlew :shared:publishToMavenLocalThen depend on io.github.santimattius:ktor-persistent-cache:1.0.0-SNAPSHOT with mavenLocal() in
your project.
The shared module is published with
the gradle-maven-publish-plugin.
| Action | Command / Doc |
|---|---|
| Publish to local Maven | ./gradlew :shared:publishToMavenLocal |
| Publish to Maven Central | See docs/PUBLISHING.md for credentials and steps. |
Coordinates and POM are configured in shared/build.gradle.kts.
This project is licensed under the Apache License, Version 2.0.
| Resource | URL |
|---|---|
| Ktor — HTTP client | ktor.io/docs/client |
| Ktor — Caching | ktor.io/docs/client-caching |
| Okio | github.com/square/okio |
| Kotlin Multiplatform | kotlinlang.org/docs/multiplatform |
| Publishing (this repo) | docs/PUBLISHING.md |
A Kotlin Multiplatform library that adds persistent HTTP caching to Ktor HttpClient. Responses are stored on disk using Okio, with configurable size limits, TTL, and platform-appropriate cache directories.
Vary headers so different variants (e.g. by
Accept-Language) are cached separately.| Platform | Cache directory |
|---|---|
| Android | Application cache dir (context.cacheDir) |
| iOS | App caches directory (NSCachesDirectory in the sandbox) |
| JVM | java.io.tmpdir/ktor-cache |
ktor-client-core 3.4.0+) and an engine (CIO, OkHttp, etc.) for your
targetsAdd the dependency to your shared or platform source sets.
Kotlin DSL (Gradle):
repositories {
mavenCentral()
// For snapshots:
// maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
commonMain.dependencies {
implementation("io.github.santimattius:ktor-persistent-cache:1.0.0-SNAPSHOT")
}
// Also add a Ktor engine for each target, e.g.:
// implementation("io.ktor:ktor-client-okhttp") // Android
// implementation("io.ktor:ktor-client-cio") // iOS / JVM
}Version catalog (e.g. libs.versions.toml):
[versions]
ktorPersistentCache = "1.0.0-SNAPSHOT"
[libraries]
ktor-persistent-cache = { group = "io.github.santimattius", name = "ktor-persistent-cache", version.ref = "ktorPersistentCache" }Then:
implementation(libs.ktor.persistent.cache)The library needs the application context to resolve the cache directory. The recommended way is App Startup:
Merge the library’s manifest
The shared (or Android) module that depends on ktor-persistent-cache should merge the
library’s AndroidManifest so that the App Startup InitializationProvider and
ContextInitializer are registered.
No extra code
If the manifest is merged, ContextInitializer runs at app startup and injects the application
context. getCacheDirectoryProvider()
will then use it automatically.
If you don’t use the library’s manifest (e.g. you use a different DI or startup path), you must call once at app startup with the application context (not an Activity context):
// e.g. in Application.onCreate()
injectContext(applicationContext)injectContext is provided by the library package
io.github.santimattius.persistent.cache.startup.
No setup. The library uses the default app caches directory.
No setup. The library uses a subdirectory of the JVM temp directory.
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.github.santimattius.persistent.cache.*
val client = HttpClient(CIO) {
installPersistentCache(
CacheConfig(
enabled = true,
cacheDirectory = "http_cache",
maxCacheSize = 10L * 1024 * 1024, // 10 MB
cacheTtl = 60 * 60 * 1000, // 1 hour
isShared = true,
isPublic = false
)
)
}val response: String = client.get("https://example.com/api/data").body()CacheConfig supports:
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
Boolean |
false |
Whether the HTTP cache is enabled. |
cacheDirectory |
String |
"http_cache" |
Name of the cache directory under the platform cache root. |
maxCacheSize |
Long |
10 MB | Maximum cache size in bytes. LRU eviction when exceeded. Use 0 for no limit. |
cacheTtl |
Long |
1 hour | Time-to-live for entries in milliseconds. |
isShared |
Boolean |
true |
Whether the cache is shared across requests (Ktor HttpCache behavior). |
isPublic |
Boolean |
false |
When true, cached responses are treated as public (shareable across users); when false, they are private to the client. |
Convenience constructor:
CacheConfig(enabled = true, cacheDirectory = "my_cache")
// Other properties use defaults.To control where the cache is stored (e.g. a custom folder or test directory), implement CacheDirectoryProvider and pass it to installPersistentCache:
val customProvider = object : CacheDirectoryProvider {
override val cacheDirectory: Path get() = FileSystem.SYSTEM.toPath("/custom/cache/dir")
}
HttpClient(CIO) {
installPersistentCache(
config = CacheConfig(enabled = true, cacheDirectory = "http_cache"),
cacheDirectoryProvider = customProvider
)
}Default behavior (no custom provider): getCacheDirectoryProvider() returns the platform implementation (Android app cache dir, iOS caches dir, or JVM temp dir).
Build:
./gradlew :shared:compileKotlinIosSimulatorArm64 :shared:compileAndroidMain
# or
./gradlew :shared:assembleRun tests:
./gradlew testPublish to local Maven:
./gradlew :shared:publishToMavenLocalThen depend on io.github.santimattius:ktor-persistent-cache:1.0.0-SNAPSHOT with mavenLocal() in
your project.
The shared module is published with
the gradle-maven-publish-plugin.
| Action | Command / Doc |
|---|---|
| Publish to local Maven | ./gradlew :shared:publishToMavenLocal |
| Publish to Maven Central | See docs/PUBLISHING.md for credentials and steps. |
Coordinates and POM are configured in shared/build.gradle.kts.
This project is licensed under the Apache License, Version 2.0.
| Resource | URL |
|---|---|
| Ktor — HTTP client | ktor.io/docs/client |
| Ktor — Caching | ktor.io/docs/client-caching |
| Okio | github.com/square/okio |
| Kotlin Multiplatform | kotlinlang.org/docs/multiplatform |
| Publishing (this repo) | docs/PUBLISHING.md |