Sample App
A runnable Compose Multiplatform sample lives in the repo's sample/ folder —
sample/shared/ holds one shared UI and business logic for both platforms,
sample/androidApp/ is a thin Android wrapper, and sample/iosApp/ is a
real Xcode project that embeds the shared Kotlin framework (via Gradle's
embedAndSignAppleFrameworkForXcode task — no CocoaPods on the sample's own side). It
runs on both Android and iOS.
What it does
A home screen lists five use cases, each a real, working screen backed by the real
Documents API:
- Settings & Preferences — typed, observable app settings. A
SettingsRepositorywrapsDocuments.document<AppSettings>("app-settings"), usingfield()/fieldFlow()delegates fortheme(SYSTEM/LIGHT/DARK) andlocale; changing either persists instantly and updates the screen's own reactive readout with no manual refresh. - Session & User State — sign in/out, with an encrypted auth token. A
SessionRepositoryopensDocuments.collection("session")configured with a realFieldDecorator(AesGcmFieldDecorator, AES-GCM via cryptography-kotlin) wrappingauthToken. A "Prove authToken is encrypted at rest" action opens a second document with a different AES key and shows the decrypt failure — concrete proof the stored bytes are ciphertext, not the plaintext token. - Caches & Drafts — a draft that survives relaunch, in its own store. A
CacheRepositoryopensDocuments.collection("cache")holdingSyncStateandDraftPost; the draft text field writes on a debounce and restores on screen entry. An isolation check on the same screen shows the Settings screen'sthemevalue (a different collection) staying untouched when "Clear cache" runs. - Reactive UI State — a simulated download driving a live progress bar. A
coroutine ticker calls
update(DownloadState::bytesDownloaded, bytesSoFar)repeatedly; aLinearProgressIndicatorre-renders purely by collecting the document'sflow(), with zero polling and zero coupling to where the write came from. - Shared KMP Persistence — one offline queue, identical code on both
platforms. A
PendingRequestQueuewrapsDocuments.document<QueueState>("pending-requests")(the queue's items live inside a small wrapper class, not as a bareListdocument root — see Opening Documents for why) with "Enqueue mock request" and "Clear queue" actions and a live list. This screen's own code is entirelycommonMain— nothing platform-specific was written to make it work on both Android and iOS.
Run it
Clone the repository. For Android, run the sample/androidApp module from Android
Studio or via Gradle. For iOS, open
sample/iosApp/iosApp.xcodeproj in Xcode and run. See
Use Cases for the documented pattern each screen demonstrates.