
Lightweight wrapper simplifying Firebase usage: Firestore CRUD and reactive flows with serializable models, typed Remote Config with safe defaults, callable Cloud Functions, analytics/crashlytics access, timestamp conversion and Napier logging.
KMPFirebase is a Kotlin Multiplatform library for Android and iOS which wraps the GitLive Firebase Kotlin SDK and provides a more straightforward implementation for the Firebase services most apps use: Firestore, Remote Config, Cloud Functions, Analytics and Crashlytics.
Be sure to show your support by starring ⭐️ this repository!
@Serializable models.Timestamp to and from kotlinx.datetime LocalDate and LocalDateTime.settings.gradle.kts file, add Maven Central to your repositories:repositories {
mavenCentral()
}commonMain source set.Open libs.versions.toml:
[versions]
kmpFirebase = "1.6.0" // Check latest version
[libraries]
kmpfirebase = { group = "io.github.tweener", name = "kmp-firebase", version.ref = "kmpFirebase" }Then in your module build.gradle.kts add:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.kmpfirebase)
}
}
}In your module build.gradle.kts add:
kotlin {
sourceSets {
commonMain.dependencies {
val kmpFirebase_version = "1.6.0" // Check latest version
implementation("io.github.tweener:kmp-firebase:$kmpFirebase_version")
}
}
}iosArm64 and iosSimulatorArm64.minSdk 24, and your app must compile against Android API 37 or later (compileSdk = 37).@Serializable, so apply the org.jetbrains.kotlin.plugin.serialization Gradle plugin to the module declaring them.KMPFirebase exposes the GitLive Firebase SDK modules it uses (firebase-firestore, firebase-config, firebase-functions, firebase-crashlytics and firebase-analytics) as api dependencies, so your app sets Firebase up the way the GitLive SDK requires:
google-services.json and apply the Google Services Gradle plugin.FirebaseApp.configure() at launch, before using any Firebase API.See the GitLive Firebase Kotlin SDK README for the full setup and the supported Firebase iOS SDK versions.
To see KMPFirebase logs, initialize Napier in your app, for instance with Napier.base(DebugAntilog()).
Declare your models by extending FirestoreModel, which holds the Firestore document ID:
@Serializable
data class City(
override var id: String = "",
val name: String,
val population: Long,
) : FirestoreModel()Then use FirebaseFirestoreService to read and write documents:
val firestoreService = FirebaseFirestoreService()
// Read all documents of a collection, with their document ID set in `id`
val cities: List<City> = firestoreService.getAll<City>(collection = "cities")
// Read a single document
val city: City = firestoreService.get<City>(collection = "cities", id = "LA")
// Listen to a single document
firestoreService.getAsFlow<City>(collection = "cities", id = "LA")
.collect { city -> println(city.name) }
// Listen to a collection: the first emission holds every document, the next ones only the documents that changed
firestoreService.getAllAsFlow<City>(collection = "cities")
.collect { changedCities -> println(changedCities.size) }
// Create a document with a given ID, and get it back
val createdCity: City = firestoreService.create<City>(collection = "cities", id = "SF", data = mapOf("name" to "San Francisco", "population" to 808_437L))
// Add a document with an ID generated by Firestore
firestoreService.add(collection = "cities", data = mapOf("name" to "Tokyo", "population" to 14_187_176L))
// Update some fields of a document
firestoreService.update(collection = "cities", id = "SF", data = mapOf("population" to 808_988L))
// Delete a document
firestoreService.delete(collection = "cities", id = "SF")Only getAll and getAllAsFlow set id from the Firestore document ID. get, getAsFlow and create read id from the document data, so store it in the document if you need it there.
The suspend functions log and rethrow any error, so wrap them in a try/catch where needed.
Convert between Firestore Timestamp and kotlinx.datetime types:
val timestamp: Timestamp = LocalDateTime(2026, 11, 5, 9, 30).toTimestamp()
val dateTimestamp: Timestamp = LocalDate(2026, 11, 5).toTimestamp()
val localDateTime: LocalDateTime = timestamp.toLocalDateTime()
val localDate: LocalDate = timestamp.toLocalDate()FirebaseRemoteConfigService fetches and activates the Remote Config values on every read. Pass isDebug = true to lower the minimum fetch interval to 10 seconds while developing.
FirebaseRemoteConfigDataSource reads typed values, and returns defaultValue if the value can't be fetched or read:
val remoteConfigService = FirebaseRemoteConfigService(isDebug = BuildConfig.DEBUG)
val remoteConfig = FirebaseRemoteConfigDataSource(firebaseRemoteConfigService = remoteConfigService)
val isFeatureEnabled: Boolean = remoteConfig.getBoolean(key = "feature_enabled", defaultValue = false)
val maxItems: Long = remoteConfig.getLong(key = "max_items", defaultValue = 10L)
val welcomeMessage: String = remoteConfig.getString(key = "welcome_message", defaultValue = "Welcome!")For any other type, call remoteConfigService.get<T>(key, defaultValue) directly.
FirebaseFunctionsService calls an HTTPS callable function with a 30-second timeout, and returns null if the call fails:
@Serializable
data class GreetingRequest(val name: String)
@Serializable
data class GreetingResponse(val message: String)
val functionsService = FirebaseFunctionsService()
val response: GreetingResponse? = functionsService.callFunction<GreetingRequest, GreetingResponse>(
functionName = "greet",
data = GreetingRequest(name = "Ada"),
)Get the GitLive Firebase instances to use their full API:
val analytics: FirebaseAnalytics = FirebaseAnalyticsService().getAnalytics()
analytics.logEvent("city_viewed", mapOf("city" to "LA"))
val crashlytics: FirebaseCrashlytics = FirebaseCrashlyticsService().getCrashlytics()
crashlytics.recordException(exception)KMPFirebase is licensed under the Apache-2.0.
KMPFirebase is a Kotlin Multiplatform library for Android and iOS which wraps the GitLive Firebase Kotlin SDK and provides a more straightforward implementation for the Firebase services most apps use: Firestore, Remote Config, Cloud Functions, Analytics and Crashlytics.
Be sure to show your support by starring ⭐️ this repository!
@Serializable models.Timestamp to and from kotlinx.datetime LocalDate and LocalDateTime.settings.gradle.kts file, add Maven Central to your repositories:repositories {
mavenCentral()
}commonMain source set.Open libs.versions.toml:
[versions]
kmpFirebase = "1.6.0" // Check latest version
[libraries]
kmpfirebase = { group = "io.github.tweener", name = "kmp-firebase", version.ref = "kmpFirebase" }Then in your module build.gradle.kts add:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.kmpfirebase)
}
}
}In your module build.gradle.kts add:
kotlin {
sourceSets {
commonMain.dependencies {
val kmpFirebase_version = "1.6.0" // Check latest version
implementation("io.github.tweener:kmp-firebase:$kmpFirebase_version")
}
}
}iosArm64 and iosSimulatorArm64.minSdk 24, and your app must compile against Android API 37 or later (compileSdk = 37).@Serializable, so apply the org.jetbrains.kotlin.plugin.serialization Gradle plugin to the module declaring them.KMPFirebase exposes the GitLive Firebase SDK modules it uses (firebase-firestore, firebase-config, firebase-functions, firebase-crashlytics and firebase-analytics) as api dependencies, so your app sets Firebase up the way the GitLive SDK requires:
google-services.json and apply the Google Services Gradle plugin.FirebaseApp.configure() at launch, before using any Firebase API.See the GitLive Firebase Kotlin SDK README for the full setup and the supported Firebase iOS SDK versions.
To see KMPFirebase logs, initialize Napier in your app, for instance with Napier.base(DebugAntilog()).
Declare your models by extending FirestoreModel, which holds the Firestore document ID:
@Serializable
data class City(
override var id: String = "",
val name: String,
val population: Long,
) : FirestoreModel()Then use FirebaseFirestoreService to read and write documents:
val firestoreService = FirebaseFirestoreService()
// Read all documents of a collection, with their document ID set in `id`
val cities: List<City> = firestoreService.getAll<City>(collection = "cities")
// Read a single document
val city: City = firestoreService.get<City>(collection = "cities", id = "LA")
// Listen to a single document
firestoreService.getAsFlow<City>(collection = "cities", id = "LA")
.collect { city -> println(city.name) }
// Listen to a collection: the first emission holds every document, the next ones only the documents that changed
firestoreService.getAllAsFlow<City>(collection = "cities")
.collect { changedCities -> println(changedCities.size) }
// Create a document with a given ID, and get it back
val createdCity: City = firestoreService.create<City>(collection = "cities", id = "SF", data = mapOf("name" to "San Francisco", "population" to 808_437L))
// Add a document with an ID generated by Firestore
firestoreService.add(collection = "cities", data = mapOf("name" to "Tokyo", "population" to 14_187_176L))
// Update some fields of a document
firestoreService.update(collection = "cities", id = "SF", data = mapOf("population" to 808_988L))
// Delete a document
firestoreService.delete(collection = "cities", id = "SF")Only getAll and getAllAsFlow set id from the Firestore document ID. get, getAsFlow and create read id from the document data, so store it in the document if you need it there.
The suspend functions log and rethrow any error, so wrap them in a try/catch where needed.
Convert between Firestore Timestamp and kotlinx.datetime types:
val timestamp: Timestamp = LocalDateTime(2026, 11, 5, 9, 30).toTimestamp()
val dateTimestamp: Timestamp = LocalDate(2026, 11, 5).toTimestamp()
val localDateTime: LocalDateTime = timestamp.toLocalDateTime()
val localDate: LocalDate = timestamp.toLocalDate()FirebaseRemoteConfigService fetches and activates the Remote Config values on every read. Pass isDebug = true to lower the minimum fetch interval to 10 seconds while developing.
FirebaseRemoteConfigDataSource reads typed values, and returns defaultValue if the value can't be fetched or read:
val remoteConfigService = FirebaseRemoteConfigService(isDebug = BuildConfig.DEBUG)
val remoteConfig = FirebaseRemoteConfigDataSource(firebaseRemoteConfigService = remoteConfigService)
val isFeatureEnabled: Boolean = remoteConfig.getBoolean(key = "feature_enabled", defaultValue = false)
val maxItems: Long = remoteConfig.getLong(key = "max_items", defaultValue = 10L)
val welcomeMessage: String = remoteConfig.getString(key = "welcome_message", defaultValue = "Welcome!")For any other type, call remoteConfigService.get<T>(key, defaultValue) directly.
FirebaseFunctionsService calls an HTTPS callable function with a 30-second timeout, and returns null if the call fails:
@Serializable
data class GreetingRequest(val name: String)
@Serializable
data class GreetingResponse(val message: String)
val functionsService = FirebaseFunctionsService()
val response: GreetingResponse? = functionsService.callFunction<GreetingRequest, GreetingResponse>(
functionName = "greet",
data = GreetingRequest(name = "Ada"),
)Get the GitLive Firebase instances to use their full API:
val analytics: FirebaseAnalytics = FirebaseAnalyticsService().getAnalytics()
analytics.logEvent("city_viewed", mapOf("city" to "LA"))
val crashlytics: FirebaseCrashlytics = FirebaseCrashlyticsService().getCrashlytics()
crashlytics.recordException(exception)KMPFirebase is licensed under the Apache-2.0.