Documents
Typed, reactive documents on top of MMKV — fast and a joy to write. Define a data
class, treat it as a document, and get typed reads, copy-style updates, and Flow
reactivity — all riding on the fastest key-value engine on mobile.
@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.update { current -> // several fields, one atomic write
current.copy(
title = "Pick up oat milk",
body = "The barista-approved kind",
)
}
note.flow().collect { editor.render(it) } // the editor reacts to every write
Built for elegancy
MMKV is ridiculously fast — memory-mapped, protobuf-backed, about as quick as anything else on the platform. But out of the box it hands you a bare cupboard: raw keys, primitives only. Every typed object means hand-rolling serialization and key management again, from scratch, every time. The typed and reactive alternatives buy their comfort back by giving up that speed, so you end up picking a side — fast, or pleasant to use.
Documents refuses to pick a side. It's built for elegancy: a clean, idiomatic
Kotlin surface — typed documents, property delegates, a copy()-style update DSL,
Flow reactivity — sitting directly on top of MMKV, costing you a little CPU for
serialization and never a single extra byte of I/O. Clean code on top, MMKV all the way down.
The problem
Android and Kotlin Multiplatform apps constantly need to persist a single record of structured
data — a logged-in user, a settings object, a session, a draft — and none of the usual tools fit
that shape well. SharedPreferences is key-value only, awkward for nested objects, and
loads the whole file into memory. Jetpack DataStore is typed, but Proto demands schema files and
Preferences is still flat key-value — heavy ceremony just to store one object. SQLite and Room are
relational and table-oriented, overkill for a single document with no relations to speak of. And
raw MMKV is blisteringly fast but offers no abstraction at all: you're on your own for
serialization and key management. There was no library giving a document-oriented, typed,
reactive API with MMKV's performance underneath — so Documents is that intersection.
What it gives you
- A document-oriented public API —
get,set,update,delete,exists,flow,stateFlow. - Typed persistence of any
@Serializableobject, with no per-type boilerplate. - Reactivity — observers are notified when a document or a single field changes.
- MMKV-class performance — the abstraction adds CPU/memory cost only, never extra I/O.
- A fully KMP-compatible public API living in
commonMain, with only the storage engine binding per platform. - Serialization handled internally via a compact CBOR format — zero schema, zero per-type boilerplate to babysit.
Single-process only
Every store — the default store, every named collection, and the in-memory test backend alike — is single-process. Concurrent access to the same store from more than one OS process is not supported and can corrupt it. A background service, an app extension, a second process spawned for any reason — none of these may open the same store as your main process at the same time.
This is not a soft caveat or an edge case to work around: if your app needs to share a store
across processes, this library is not the right fit for that store today. Design around a
single-process owner for each store, or keep multi-process data out of Documents
entirely.
Built by
Where to next
- Installation — add the dependency, zero setup ceremony.
- Quick Start — the whole API in one short walkthrough.
- Guide — task-oriented walkthroughs of every part of the API.