Getting Started
Build Requirements
- JDK 17+ (required by AGP 8.x)
- Android SDK for Android builds
- Xcode + Command Line Tools for iOS builds
Installation
This library is available on Maven Central.
Method 1: Basic Setup (Groovy/Kotlin DSL)
Use the <version> placeholder with the latest release version (e.g. 1.0.0). Check the badge in README for the latest version.
Core Library (Required)
No special repository configuration is needed if you already have mavenCentral().
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
// build.gradle.kts (commonMain)
implementation("io.github.parkwoocheol:kmp-datastore:<version>")
// Optional: for Kotlinx Serialization support
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
Method 2: Version Catalog (libs.versions.toml)
For modern projects, add the following to your libs.versions.toml:
[versions]
kmpDatastore = "<version>" # e.g. 1.0.0
[libraries]
kmp-datastore = { module = "io.github.parkwoocheol:kmp-datastore", version.ref = "kmpDatastore" }
kmp-datastore-annotations = { module = "io.github.parkwoocheol:kmp-datastore-annotations", version.ref = "kmpDatastore" }
kmp-datastore-ksp = { module = "io.github.parkwoocheol:kmp-datastore-ksp", version.ref = "kmpDatastore" }
Then use it in your build.gradle.kts:
Platform-Specific Setup
Android
Initialize the context in your Application class:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
KmpDataStoreContext.init(this)
}
}
Don't forget to register it in AndroidManifest.xml:
iOS
No setup required! DataStore automatically uses NSUserDefaults internally.
Desktop (JVM)
No setup required! DataStore uses file-based storage in ~/.config/{app}/datastore/.
Basic Usage
1. Create a DataStore
// For primitive types only (no serializer needed)
val dataStore = TypeSafeDataStore("user_preferences")
// For object storage (with optional serializer)
val serializer = KotlinxDataStoreSerializer()
val objectStore = TypeSafeDataStore("app_data", serializer)
2. Store and Retrieve Primitives
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
// Store primitives
runBlocking {
dataStore.putInt("age", 25)
dataStore.putString("username", "john_doe")
dataStore.putBoolean("is_premium", true)
dataStore.putStringSet("tags", setOf("kotlin", "multiplatform"))
}
// Retrieve primitives
dataStore.getInt("age").collect { age ->
println("Age: $age") // Age: 25
}
dataStore.getString("username").collect { name ->
println("Username: $name") // Username: john_doe
}
dataStore.getBoolean("is_premium").collect { isPremium ->
println("Premium: $isPremium") // Premium: true
}
3. Store and Retrieve Objects
import kotlinx.serialization.Serializable
@Serializable
data class User(
val name: String,
val age: Int,
val email: String
)
// With serializer
val serializer = KotlinxDataStoreSerializer()
val dataStore = TypeSafeDataStore("app_data", serializer)
// Store object
val user = User("John Doe", 25, "john@example.com")
dataStore.put("current_user", user)
// Retrieve object
dataStore.get<User>("current_user").collect { user ->
println("User: ${user?.name}, Age: ${user?.age}")
// User: John Doe, Age: 25
}
4. Remove and Clear
// Remove a single key
dataStore.remove("username")
// Clear all data
dataStore.clear()
// Get all keys
dataStore.getAllKeys().collect { keys ->
println("All keys: $keys")
}
Query Operations
Pattern Matching
// Find all keys starting with "user_"
dataStore.select("user_*").collect { keys ->
println("User keys: $keys")
}
// Keys ending with "_id"
dataStore.keysEndingWith("_id").collect { idKeys ->
println("ID keys: $idKeys")
}
// Keys containing "temp"
dataStore.keysContaining("temp").collect { tempKeys ->
println("Temporary keys: $tempKeys")
}
Value-Based Filtering
// Filter by string value
dataStore.filterByValue<String> { key, value ->
value.contains("john", ignoreCase = true)
}.collect { matchingKeys ->
println("Keys with 'john': $matchingKeys")
}
// Filter by int value
dataStore.filterByValue<Int> { key, value ->
value > 18
}.collect { adultKeys ->
println("Keys with value > 18: $adultKeys")
}
// Filter by boolean value
dataStore.filterByValue<Boolean> { key, value ->
value == true
}.collect { trueKeys ->
println("Keys with true value: $trueKeys")
}
Key + Value Query Builder
dataStore.queryValues<String>()
.startsWith("user_")
.valueContains("john")
.sortByValueAscending()
.take(10)
.executeMap()
.collect { results ->
println("Top results: $results")
}
Note: Key + value queries perform an in-memory scan of all keys and read each value.
Use key-only queries when performance matters.
For non-primitive types (including collections other than Set<String>), a serializer is required.
Search and Grouping
// Search string values
dataStore.searchStringValues("search term").collect { results ->
results.forEach { (key, value) ->
println("Found: $key = $value")
}
}
// Group keys by prefix (before delimiter)
dataStore.groupByKeyPrefix('_').collect { grouped ->
grouped.forEach { (prefix, keys) ->
println("$prefix: $keys")
}
}
// Count total keys
dataStore.count().collect { count ->
println("Total keys: $count")
}
// Check if key exists
dataStore.containsKey("username").collect { exists ->
println("Username exists: $exists")
}
Compose Integration
@Composable
fun UserProfile() {
val age by dataStore.getInt("age")
.collectAsState(initial = null)
val username by dataStore.getString("username")
.collectAsState(initial = null)
Column {
Text("Username: ${username ?: "Loading..."}")
Text("Age: ${age ?: "Loading..."}")
}
}
Error Handling
try {
dataStore.put("user", user)
} catch (e: SerializationException) {
// Handle serialization error
println("Failed to serialize: ${e.message}")
} catch (e: Exception) {
// Handle other errors
println("Error: ${e.message}")
}
Next Steps
- API Reference - Complete API documentation
- Guides - Advanced usage patterns
- SPEC.md - Full technical specification