
Typed, reactive document storage backed by MMKV offering class-as-document reads, copy-style updates, field-level mutation, and stream reactivity without schema, DAOs or serialization boilerplate.
A document-oriented Kotlin Multiplatform storage library backed by
MMKV. Define a data class, treat it as a document, and get
typed reads, copy-style updates, and Flow reactivity.
@Serializable
data class Note(val title: String = "", val body: String = "", val done: Boolean = false)
val note = Documents.document<Note>("note-1") // one call, you have a document
note.set(Note(title = "Pick up milk", body = "2%, not whole"))
note.update(Note::done, true) // one field, no read
note.flow().collect { editor.render(it) } // the editor reacts to every writeThat's the whole story. No schema, no DAO, no MMKV.initialize, no serialization plumbing.
Installation, quick start, guides, use cases, concepts, benchmarks, and the full API reference all live there.
[!NOTE] What's new in v0.2.1
Version 0.2.1 fixes a crash on Android devices that run as a 32-bit process. In v0.1.0 and v0.2.0, those devices crashed at launch, whether or not the app opened a document.
- No more launch crash. The app now starts on a 32-bit process.
Documents.document(...)andDocuments.collection(...)throw the newUnsupportedArchitectureExceptioninstead.- 32-bit is not supported. MMKV 2.x requires a 64-bit process. A separate legacy artifact is being considered, but nothing is decided.
- Nothing to change in your code. The only API change is the new exception class.
See Platform Support for details.
[!NOTE] What's new in v0.2.0
Version 0.2.0 makes it safe to open the same document in more than one place. In v0.1.0, a write was only seen by the instance that made it, so every part of an app had to share a single instance. Sharing a single instance is no longer required, but it is still the recommended approach when it is practical.
- Instances stay in sync. A write through one instance now reaches
flow(),stateFlow(), andfieldFlow()on every other instance of the same document. This holds whether the instances come from dependency injection, from repeatedDocuments.document(...)calls, or from separateDocuments.collection(name)calls.- Updates are not lost. If two instances of the same document call
updateat the same time, both changes are applied one after the other.- Clearer error messages. Each cause of a
DocumentDecodingExceptionnow has its own message that says what went wrong and how to fix it.- Nothing to change in your code. The public API is the same as in v0.1.0.
Opening Documents explains how to open the same document more than once. The Reactivity Model explains how the instances stay in sync.
| Platform | Status | Storage engine |
|---|---|---|
| Android | ✅ | MMKV |
iOS (arm64 and simulatorArm64) |
✅ | MMKV |
[!NOTE] How the library is configured. On Apple targets, MMKV is bound through the Kotlin CocoaPods plugin.
Both platforms share one public API, and all of it lives in commonMain. On both platforms the
library initializes MMKV itself, so you have nothing to set up for it.
[!IMPORTANT] Android is 64-bit only. MMKV 2.x does not support 32-bit, so 32-bit processes are not supported. 0.1.0 and 0.2.0 crash at launch on them. From 0.2.1 the app starts and
Documents.documentandDocuments.collectionthrowUnsupportedArchitectureException. See Platform Support.
A runnable Compose Multiplatform sample lives in sample/. It runs on both Android and
iOS, and it is made of three parts:
sample/shared/: one shared UI that runs natively on both platforms.sample/androidApp/: the Android app.sample/iosApp/: the iOS app.It has a screen for each documented use case, plus one that shows two instances of a document staying in sync:
Clone the repository, run it, and tap through it on either platform.
PetCare is a pet care app for Android. It tracks pet health and fitness data and gives weekly AI-generated suggestions. PetCare is my own app, and it is in closed testing right now.
PetCare uses Documents in production, with one collection for each kind of data:
settings: app preferences such as theme, weight units, haptics, whether the AI chat
disclosure was acknowledged, and analytics consent. The UI collects the document's flow(), so
a change shows up everywhere right away.session: the access and refresh tokens. A FieldDecorator encrypts them with AES-GCM, and
the key is protected by the Android Keystore. Signing out only deletes the document. Other parts
of the app collect its flow() and react: one resets account-specific settings, and another
tells the server and unregisters the push token.analytics_profile: the user id and how the user signed up, kept for analytics.lifecycle_progress: how far the user has come: signed up, added a pet, logged for the
first time, or become an active logger. It also keeps the recent days they logged and whether
they have used the AI chat.It also shows two patterns:
DocumentDecodingException, deletes the document, and starts again from the defaults.Collection, so tests pass in Documents.inMemory() and need no
device.See LICENSE.
If you find Documents useful, consider giving the repo a ⭐. It helps others discover it.
A document-oriented Kotlin Multiplatform storage library backed by
MMKV. Define a data class, treat it as a document, and get
typed reads, copy-style updates, and Flow reactivity.
@Serializable
data class Note(val title: String = "", val body: String = "", val done: Boolean = false)
val note = Documents.document<Note>("note-1") // one call, you have a document
note.set(Note(title = "Pick up milk", body = "2%, not whole"))
note.update(Note::done, true) // one field, no read
note.flow().collect { editor.render(it) } // the editor reacts to every writeThat's the whole story. No schema, no DAO, no MMKV.initialize, no serialization plumbing.
Installation, quick start, guides, use cases, concepts, benchmarks, and the full API reference all live there.
[!NOTE] What's new in v0.2.1
Version 0.2.1 fixes a crash on Android devices that run as a 32-bit process. In v0.1.0 and v0.2.0, those devices crashed at launch, whether or not the app opened a document.
- No more launch crash. The app now starts on a 32-bit process.
Documents.document(...)andDocuments.collection(...)throw the newUnsupportedArchitectureExceptioninstead.- 32-bit is not supported. MMKV 2.x requires a 64-bit process. A separate legacy artifact is being considered, but nothing is decided.
- Nothing to change in your code. The only API change is the new exception class.
See Platform Support for details.
[!NOTE] What's new in v0.2.0
Version 0.2.0 makes it safe to open the same document in more than one place. In v0.1.0, a write was only seen by the instance that made it, so every part of an app had to share a single instance. Sharing a single instance is no longer required, but it is still the recommended approach when it is practical.
- Instances stay in sync. A write through one instance now reaches
flow(),stateFlow(), andfieldFlow()on every other instance of the same document. This holds whether the instances come from dependency injection, from repeatedDocuments.document(...)calls, or from separateDocuments.collection(name)calls.- Updates are not lost. If two instances of the same document call
updateat the same time, both changes are applied one after the other.- Clearer error messages. Each cause of a
DocumentDecodingExceptionnow has its own message that says what went wrong and how to fix it.- Nothing to change in your code. The public API is the same as in v0.1.0.
Opening Documents explains how to open the same document more than once. The Reactivity Model explains how the instances stay in sync.
| Platform | Status | Storage engine |
|---|---|---|
| Android | ✅ | MMKV |
iOS (arm64 and simulatorArm64) |
✅ | MMKV |
[!NOTE] How the library is configured. On Apple targets, MMKV is bound through the Kotlin CocoaPods plugin.
Both platforms share one public API, and all of it lives in commonMain. On both platforms the
library initializes MMKV itself, so you have nothing to set up for it.
[!IMPORTANT] Android is 64-bit only. MMKV 2.x does not support 32-bit, so 32-bit processes are not supported. 0.1.0 and 0.2.0 crash at launch on them. From 0.2.1 the app starts and
Documents.documentandDocuments.collectionthrowUnsupportedArchitectureException. See Platform Support.
A runnable Compose Multiplatform sample lives in sample/. It runs on both Android and
iOS, and it is made of three parts:
sample/shared/: one shared UI that runs natively on both platforms.sample/androidApp/: the Android app.sample/iosApp/: the iOS app.It has a screen for each documented use case, plus one that shows two instances of a document staying in sync:
Clone the repository, run it, and tap through it on either platform.
PetCare is a pet care app for Android. It tracks pet health and fitness data and gives weekly AI-generated suggestions. PetCare is my own app, and it is in closed testing right now.
PetCare uses Documents in production, with one collection for each kind of data:
settings: app preferences such as theme, weight units, haptics, whether the AI chat
disclosure was acknowledged, and analytics consent. The UI collects the document's flow(), so
a change shows up everywhere right away.session: the access and refresh tokens. A FieldDecorator encrypts them with AES-GCM, and
the key is protected by the Android Keystore. Signing out only deletes the document. Other parts
of the app collect its flow() and react: one resets account-specific settings, and another
tells the server and unregisters the push token.analytics_profile: the user id and how the user signed up, kept for analytics.lifecycle_progress: how far the user has come: signed up, added a pet, logged for the
first time, or become an active logger. It also keeps the recent days they logged and whether
they have used the AI chat.It also shows two patterns:
DocumentDecodingException, deletes the document, and starts again from the defaults.Collection, so tests pass in Documents.inMemory() and need no
device.See LICENSE.
If you find Documents useful, consider giving the repo a ⭐. It helps others discover it.