
Short for Compose Multiplatform Preferences
A lightweight, developer-friendly Key-Value Storage library for Kotlin Multiplatform (KMP) and Compose Multiplatform (CMP) projects. It allows you to read, write, and manage preferences seamlessly across multiple targets (Android, iOS, Desktop/JVM, Web JS & Wasm) using a unified API and elegant Kotlin property delegates.
If you are familiar with the popular multiplatform-settings library, here is how Compose Multiplatform Preferences compares and why it might be the right choice for your project:
| Feature | multiplatform-settings |
compose-multiplatform-preferences (Ours) |
|---|---|---|
| Simple Key-Value Save | ✅ Yes | ✅ Yes |
| Kotlin Property Delegates | ✅ Yes | ✅ Yes |
| Out-of-the-box UI Editor | ❌ No | ✅ Yes (Drop-in Composable UI component) |
| Instant Compose Previews | ❌ Harder |
✅ Easy (via InMemoryKeyValueStorage) |
| Dynamic Key Namespacing | ❌ Manual |
✅ Easy (via NamespacedKeyValueStorage) |
To use this library in your Kotlin Multiplatform project:
Add JitPack to your dependency resolution repositories in settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Add the dependency to the commonMain source set in your shared module's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.kvarun701.compose-multiplatform-preferences:compose-pref:1.0.0")
}
}
}This library leverages native storage mechanisms under the hood for maximum performance, robustness, and platform idiomatic behavior:
| Platform | Target | Underlying Storage Engine |
|---|---|---|
| Android | androidMain |
android.content.SharedPreferences |
| iOS | iosMain |
NSUserDefaults (with customizable suite name) |
| Desktop | jvmMain |
java.util.prefs.Preferences |
| Web |
jsMain / wasmJsMain
|
window.localStorage |
On Android, KeyValueStorageFactory requires a Context parameter to initialize SharedPreferences:
// In your Android application or Activity
val factory = KeyValueStorageFactory(context)
val storage: KeyValueStorage = factory.create(name = "my_app_preferences")For all other platforms, the factory does not require any parameters:
val factory = KeyValueStorageFactory()
val storage: KeyValueStorage = factory.create(name = "my_app_preferences")The KeyValueStorage interface provides direct, type-safe getter and setter methods:
// Writing values
storage.putString("username", "JohnDoe")
storage.putInt("login_count", 5)
storage.putBoolean("is_premium", true)
// Reading values with default values
val username = storage.getString("username", defaultValue = "Guest")
val loginCount = storage.getInt("login_count", defaultValue = 0)
val isPremium = storage.getBoolean("is_premium", defaultValue = false)
// Checking presence and removal
if (storage.contains("username")) {
storage.remove("username")
}
// Clear all preferences
storage.clear()You can declare preferences as property delegates to read and write them like regular properties. The delegate automatically updates and fetches from the underlying storage.
import com.ganesh.composepref.*
class UserSettings(storage: KeyValueStorage) {
// Read-write string delegate
var username by storage.string(key = "pref_username", defaultValue = "Guest")
// Read-write integer delegate
var score by storage.int(key = "pref_high_score", defaultValue = 0)
// Read-write boolean delegate
var isDarkTheme by storage.boolean(key = "pref_dark_theme", defaultValue = false)
}Usage in code:
val settings = UserSettings(storage)
// Read values
println(settings.username) // Prints "Guest" (or the saved value)
// Write values (writes to disk/storage instantly)
settings.username = "JaneDoe"
settings.isDarkTheme = trueIf you need to partition or isolate preferences (e.g. per-user settings, different modules), use NamespacedKeyValueStorage. It acts as a wrapper and prefixes all keys automatically:
val mainStorage = KeyValueStorageFactory().create("app_settings")
// Wrap storage with a custom namespace
val userAStorage = NamespacedKeyValueStorage(mainStorage, namespace = "user_A")
val userBStorage = NamespacedKeyValueStorage(mainStorage, namespace = "user_B")
// Keys will be stored as "ns.user_A.theme" and "ns.user_B.theme"
userAStorage.putString("theme", "dark")
userBStorage.putString("theme", "light")This library provides a fully functional Compose Multiplatform preferences editor/manager UI component out of the box. You can easily integrate this component into your UI to allow users to view, add, modify, and delete preferences under different namespaces:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.ganesh.composepref.App
import com.ganesh.composepref.KeyValueStorageFactory
@Composable
fun SettingsScreen() {
// 1. Initialize your platform-specific KeyValueStorage
val factory = remember { KeyValueStorageFactory() } // (Pass context if on Android)
val myStorage = remember { factory.create("my_settings") }
// 2. Render the interactive preferences management interface
App(storage = myStorage)
}Use InMemoryKeyValueStorage to mock settings or preferences during unit tests or Compose UI Previews without touching disk/local storage.
@Composable
@Preview
fun AppPreview() {
// Pass in-memory storage for safe, sandboxed previews
val previewStorage = InMemoryKeyValueStorage()
App(storage = previewStorage)
}You can verify the library logic on each platform using Gradle tasks:
./gradlew :shared:testAndroidHostTest
./gradlew :shared:jvmTest
./gradlew :shared:wasmJsTest
./gradlew :shared:jsTest
./gradlew :shared:iosSimulatorArm64Test
A lightweight, developer-friendly Key-Value Storage library for Kotlin Multiplatform (KMP) and Compose Multiplatform (CMP) projects. It allows you to read, write, and manage preferences seamlessly across multiple targets (Android, iOS, Desktop/JVM, Web JS & Wasm) using a unified API and elegant Kotlin property delegates.
If you are familiar with the popular multiplatform-settings library, here is how Compose Multiplatform Preferences compares and why it might be the right choice for your project:
| Feature | multiplatform-settings |
compose-multiplatform-preferences (Ours) |
|---|---|---|
| Simple Key-Value Save | ✅ Yes | ✅ Yes |
| Kotlin Property Delegates | ✅ Yes | ✅ Yes |
| Out-of-the-box UI Editor | ❌ No | ✅ Yes (Drop-in Composable UI component) |
| Instant Compose Previews | ❌ Harder |
✅ Easy (via InMemoryKeyValueStorage) |
| Dynamic Key Namespacing | ❌ Manual |
✅ Easy (via NamespacedKeyValueStorage) |
To use this library in your Kotlin Multiplatform project:
Add JitPack to your dependency resolution repositories in settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Add the dependency to the commonMain source set in your shared module's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.kvarun701.compose-multiplatform-preferences:compose-pref:1.0.0")
}
}
}This library leverages native storage mechanisms under the hood for maximum performance, robustness, and platform idiomatic behavior:
| Platform | Target | Underlying Storage Engine |
|---|---|---|
| Android | androidMain |
android.content.SharedPreferences |
| iOS | iosMain |
NSUserDefaults (with customizable suite name) |
| Desktop | jvmMain |
java.util.prefs.Preferences |
| Web |
jsMain / wasmJsMain
|
window.localStorage |
On Android, KeyValueStorageFactory requires a Context parameter to initialize SharedPreferences:
// In your Android application or Activity
val factory = KeyValueStorageFactory(context)
val storage: KeyValueStorage = factory.create(name = "my_app_preferences")For all other platforms, the factory does not require any parameters:
val factory = KeyValueStorageFactory()
val storage: KeyValueStorage = factory.create(name = "my_app_preferences")The KeyValueStorage interface provides direct, type-safe getter and setter methods:
// Writing values
storage.putString("username", "JohnDoe")
storage.putInt("login_count", 5)
storage.putBoolean("is_premium", true)
// Reading values with default values
val username = storage.getString("username", defaultValue = "Guest")
val loginCount = storage.getInt("login_count", defaultValue = 0)
val isPremium = storage.getBoolean("is_premium", defaultValue = false)
// Checking presence and removal
if (storage.contains("username")) {
storage.remove("username")
}
// Clear all preferences
storage.clear()You can declare preferences as property delegates to read and write them like regular properties. The delegate automatically updates and fetches from the underlying storage.
import com.ganesh.composepref.*
class UserSettings(storage: KeyValueStorage) {
// Read-write string delegate
var username by storage.string(key = "pref_username", defaultValue = "Guest")
// Read-write integer delegate
var score by storage.int(key = "pref_high_score", defaultValue = 0)
// Read-write boolean delegate
var isDarkTheme by storage.boolean(key = "pref_dark_theme", defaultValue = false)
}Usage in code:
val settings = UserSettings(storage)
// Read values
println(settings.username) // Prints "Guest" (or the saved value)
// Write values (writes to disk/storage instantly)
settings.username = "JaneDoe"
settings.isDarkTheme = trueIf you need to partition or isolate preferences (e.g. per-user settings, different modules), use NamespacedKeyValueStorage. It acts as a wrapper and prefixes all keys automatically:
val mainStorage = KeyValueStorageFactory().create("app_settings")
// Wrap storage with a custom namespace
val userAStorage = NamespacedKeyValueStorage(mainStorage, namespace = "user_A")
val userBStorage = NamespacedKeyValueStorage(mainStorage, namespace = "user_B")
// Keys will be stored as "ns.user_A.theme" and "ns.user_B.theme"
userAStorage.putString("theme", "dark")
userBStorage.putString("theme", "light")This library provides a fully functional Compose Multiplatform preferences editor/manager UI component out of the box. You can easily integrate this component into your UI to allow users to view, add, modify, and delete preferences under different namespaces:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.ganesh.composepref.App
import com.ganesh.composepref.KeyValueStorageFactory
@Composable
fun SettingsScreen() {
// 1. Initialize your platform-specific KeyValueStorage
val factory = remember { KeyValueStorageFactory() } // (Pass context if on Android)
val myStorage = remember { factory.create("my_settings") }
// 2. Render the interactive preferences management interface
App(storage = myStorage)
}Use InMemoryKeyValueStorage to mock settings or preferences during unit tests or Compose UI Previews without touching disk/local storage.
@Composable
@Preview
fun AppPreview() {
// Pass in-memory storage for safe, sandboxed previews
val previewStorage = InMemoryKeyValueStorage()
App(storage = previewStorage)
}You can verify the library logic on each platform using Gradle tasks:
./gradlew :shared:testAndroidHostTest
./gradlew :shared:jvmTest
./gradlew :shared:wasmJsTest
./gradlew :shared:jsTest
./gradlew :shared:iosSimulatorArm64Test