CachedFlow
1.1.0indexedStream-based caching for asynchronous data streams with typed keys, pluggable Store persistence, configurable strategies (IF_HAVE, ONLY_REQUEST, ONLY_CACHE), optional logging and serialization helpers.
Stream-based caching for asynchronous data streams with typed keys, pluggable Store persistence, configurable strategies (IF_HAVE, ONLY_REQUEST, ONLY_CACHE), optional logging and serialization helpers.
A lightweight caching library for Kotlin Flow with first-class Kotlin Multiplatform support.
Starting with 1.1.0, CachedFlow can be used directly from commonMain in Kotlin Multiplatform and Compose Multiplatform projects.
| Module | Android | Desktop JVM | iOS |
|---|---|---|---|
cachedflow | Yes | Yes | Yes |
cachedflow-ext-serialization | Yes | Yes | Yes |
cachedflow-ext-android | Yes | No | No |
The repository also includes a shared demo that exercises the library on Android, Desktop, and iOS.
IF_HAVE, ONLY_REQUEST, ONLY_CACHEUse CachedFlow from commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("ru.dapadz:cachedflow:1.1.0")
implementation("ru.dapadz:cachedflow-ext-serialization:1.1.0")
}
}
}
If you want the Android helpers (SharedPreferenceStore and AndroidLogger), add them in androidMain:
kotlin {
sourceSets {
androidMain.dependencies {
implementation("ru.dapadz:cachedflow-ext-android:1.1.0")
}
}
}
StoreStore is the persistence abstraction used by CachedFlow:
For shared code, you can back it with any platform storage such as SharedPreferences, DataStore, NSUserDefaults, files, or your own database layer.
val store: Store = MyStore()
Cache.initialize(store)
Optionally:
Cache.initialize(store, logger = MyLogger())
val userKey = stringCacheKey("user_profile")
val ageKey = integerCacheKey("user_age")
flow { emit(fetchUserProfileFromApi()) }
.cache(userKey, CacheStrategyType.IF_HAVE)
.collect { user ->
println("User: $user")
}
| Strategy | Description |
|---|---|
IF_HAVE | Use cache if a value already exists, otherwise execute the original flow. |
You can also implement your own strategy:
abstract class CacheStrategy<T>(
protected val key: Key<T>,
protected val cachedAfterLoad: Boolean
) {
abstract suspend fun execute(currentFlow: Flow<T>): Flow<T>
}
Custom keys are supported as well:
class MyKey(name: String) : Key<MyType>(name) {
override fun isTypeOf(valueClass: KClass<*>) = valueClass == MyType::class
override suspend fun (store: Store): Flow<MyType?> = TODO()
= TODO()
}
cachedflowThe multiplatform core library. Use it from shared commonMain code.
cachedflow-ext-serializationAdds support for kotlinx.serialization:
serializableKeyserializableListKeySerializersModule for polymorphic and advanced serialization casesExample:
@Serializable
data class Dog(val name: String)
fun getGoodDog(): Flow<Dog> {
return dogRepository.getGoodDog()
.cache(serializableKey("goodDog"))
}
Polymorphic example:
cachedflow-ext-androidAndroid-only helpers:
SharedPreferenceStoreAndroidLoggerExample:
Cache.initialize(
store = SharedPreferenceStore(context = this),
logger = AndroidLogger()
)
Store abstraction for platform-specific persistenceLoggerkotlinx.serialization extension for serializable objects and listsinterface Store {
suspend fun <T : Any> get(key: StoreKey<T>): Flow<T?>
suspend fun <T : Any> save(key: StoreKey<T>, value: T)
suspend fun <T : Any> delete(key: StoreKey<T>)
suspend fun clear()
}
ONLY_REQUEST| Always execute the original flow and optionally save the result. |
ONLY_CACHE | Read from cache only. Throws when the value is missing. |
| Type | Factory |
|---|
String | stringCacheKey(name) |
Int | integerCacheKey(name) |
Long | longCacheKey(name) |
Float | floatCacheKey(name) |
Double | doubleCacheKey(name) |
Byte | byteCacheKey(name) |
Short | shortCacheKey(name) |
Char | charCacheKey(name) |
Boolean | booleanCacheKey(name) |
interface Animal {
val name: String
}
data class Dog(
override val name: String,
val isGoodBoy: Boolean
) : Animal
data class Cat(
override val name: String
) : Animal
fun getAnimals(): Flow<List<Animal>> {
return repository.getAnimals()
.cache(
serializableListKey(
name = "animals",
module = SerializersModule {
polymorphic(Animal::class) {
subclass(Cat::class)
subclass(Dog::class)
}
}
)
)
}
Surfaced from shared tags and platforms — no rankings paid for.