Firestore-backed adapter for the L1 RemoteStore SPI from moon-sync-kmp.
Use it to observe Firestore collections as Kotlin Flows without any
platform-specific glue in your feature code.
- Kotlin 2.0.20, Compose Multiplatform 1.7.x, AGP 8.6.x, Gradle 8.10, Java 21.
- A Firebase project with Firestore enabled; security rules configured.
- Valid
moonLicenseKey. - Koin 4.1.0.
(Repository setup is identical to the auth module — see auth/en.md §2.)
composeApp/build.gradle.kts (commonMain):
implementation("io.moondeveloper:moon-firestore-sync-kmp:1.0.0")google-services.jsonpresent + Google Services plugin applied.- Firestore must be initialized by FirebaseApp (automatic in most setups).
GoogleService-Info.plistadded to Xcode target.FirebaseApp.configure()inAppDelegate.
import io.moondev.firestore.sync.firestoreSyncModule
startKoin {
modules(firestoreSyncModule /*, ... */)
}FirestoreRemoteStore is bound to the RemoteStore contract inside the
module — your code only sees RemoteStore.
class NotesStore(private val store: RemoteStore) {
val notes: Flow<List<Note>> = store.observeCollection("notes")
}@Composable
fun NotesScreen(store: NotesStore = koinInject()) {
val items by store.notes.collectAsState(initial = emptyList())
LazyColumn { items(items) { n -> Text(n.title) } }
}- Empty stream /
PERMISSION_DENIED— Firestore rules rejecting the request. Verify the current user is signed in (pair withmoon-firebase-auth-kmp) and the path matches your rules. Default FirebaseApp is not initializedon iOS — you skippedFirebaseApp.configure()inAppDelegate.- Backpressure warnings in logs — the stream is hot; scope your
subscription to a
ViewModelor a ComposeLaunchedEffectso it cancels on disposal.
- Combine with moon-firebase-auth-kmp to use
uid-scoped Firestore paths. - See
SyncDemoScreen.kt.