Skip to content

Commit

Permalink
Merge branch 'master' into feature/fix-gcm-sender0id
Browse files Browse the repository at this point in the history
  • Loading branch information
nbransby authored Apr 1, 2024
2 parents 91cbba7 + a2f103d commit e316f61
Show file tree
Hide file tree
Showing 73 changed files with 2,705 additions and 1,437 deletions.
60 changes: 45 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ The following libraries are available for the various Firebase products.

| Service or Product | Gradle Dependency | API Coverage |
|---------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Authentication](https://firebase.google.com/docs/auth) | [`dev.gitlive:firebase-auth:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/1.10.4/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) |
| [Realtime Database](https://firebase.google.com/docs/database) | [`dev.gitlive:firebase-database:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-database/1.10.4/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) |
| [Cloud Firestore](https://firebase.google.com/docs/firestore) | [`dev.gitlive:firebase-firestore:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/1.10.4/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) |
| [Cloud Functions](https://firebase.google.com/docs/functions) | [`dev.gitlive:firebase-functions:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/1.10.4/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) |
| [Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [`dev.gitlive:firebase-messaging:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/1.10.4/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) |
| [Cloud Storage](https://firebase.google.com/docs/storage) | [`dev.gitlive:firebase-storage:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/1.10.4/pom) | [![40%](https://img.shields.io/badge/-40%25-orange?style=flat-square)](/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt) |
| [Installations](https://firebase.google.com/docs/projects/manage-installations) | [`dev.gitlive:firebase-installations:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-installations/1.10.4/pom) | [![90%](https://img.shields.io/badge/-90%25-green?style=flat-square)](/firebase-installations/src/commonMain/kotlin/dev/gitlive/firebase/installations/installations.kt) |
| [Remote Config](https://firebase.google.com/docs/remote-config) | [`dev.gitlive:firebase-config:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-config/1.10.4/pom) | [![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square)](/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt) |
| [Performance](https://firebase.google.com/docs/perf-mon) | [`dev.gitlive:firebase-perf:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-perf/1.10.4/pom) | [![1%](https://img.shields.io/badge/-1%25-orange?style=flat-square)](/firebase-perf/src/commonMain/kotlin/dev/gitlive/firebase/perf/performance.kt) |
| [Crashlytics](https://firebase.google.com/docs/crashlytics) | [`dev.gitlive:firebase-crashlytics:1.10.4`](https://search.maven.org/artifact/dev.gitlive/firebase-crashlytics/1.10.4/pom) | [![80%](https://img.shields.io/badge/-1%25-orange?style=flat-square)](/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt) |
| [Authentication](https://firebase.google.com/docs/auth) | [`dev.gitlive:firebase-auth:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/1.12.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) |
| [Realtime Database](https://firebase.google.com/docs/database) | [`dev.gitlive:firebase-database:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-database/1.12.0/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) |
| [Cloud Firestore](https://firebase.google.com/docs/firestore) | [`dev.gitlive:firebase-firestore:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/1.12.0/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) |
| [Cloud Functions](https://firebase.google.com/docs/functions) | [`dev.gitlive:firebase-functions:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/1.12.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) |
| [Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [`dev.gitlive:firebase-messaging:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/1.12.0/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) |
| [Cloud Storage](https://firebase.google.com/docs/storage) | [`dev.gitlive:firebase-storage:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/1.12.0/pom) | [![40%](https://img.shields.io/badge/-40%25-orange?style=flat-square)](/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt) |
| [Installations](https://firebase.google.com/docs/projects/manage-installations) | [`dev.gitlive:firebase-installations:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-installations/1.12.0/pom) | [![90%](https://img.shields.io/badge/-90%25-green?style=flat-square)](/firebase-installations/src/commonMain/kotlin/dev/gitlive/firebase/installations/installations.kt) |
| [Remote Config](https://firebase.google.com/docs/remote-config) | [`dev.gitlive:firebase-config:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-config/1.12.0/pom) | [![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square)](/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt) |
| [Performance](https://firebase.google.com/docs/perf-mon) | [`dev.gitlive:firebase-perf:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-perf/1.12.0/pom) | [![1%](https://img.shields.io/badge/-1%25-orange?style=flat-square)](/firebase-perf/src/commonMain/kotlin/dev/gitlive/firebase/perf/performance.kt) |
| [Crashlytics](https://firebase.google.com/docs/crashlytics) | [`dev.gitlive:firebase-crashlytics:1.12.0`](https://search.maven.org/artifact/dev.gitlive/firebase-crashlytics/1.12.0/pom) | [![80%](https://img.shields.io/badge/-1%25-orange?style=flat-square)](/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt) |

Is the Firebase library or API you need missing? [Create an issue](https://github.com/GitLiveApp/firebase-kotlin-sdk/issues/new?labels=API+coverage&template=increase-api-coverage.md&title=Add+%5Bclass+name%5D.%5Bfunction+name%5D+to+%5Blibrary+name%5D+for+%5Bplatform+names%5D) to request additional API coverage or be awesome and [submit a PR](https://github.com/GitLiveApp/firebase-kotlin-sdk/fork)

Expand Down Expand Up @@ -70,8 +70,8 @@ The Firebase Kotlin SDK uses Kotlin serialization to read and write custom class

```groovy
plugins {
kotlin("multiplatform") version "1.8.21" // or kotlin("jvm") or any other kotlin plugin
kotlin("plugin.serialization") version "1.8.21"
kotlin("multiplatform") version "1.9.20" // or kotlin("jvm") or any other kotlin plugin
kotlin("plugin.serialization") version "1.9.20"
}
```

Expand All @@ -85,13 +85,43 @@ data class City(val name: String)
Instances of these classes can now be passed [along with their serializer](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#introduction-to-serializers) to the SDK:

```kotlin
db.collection("cities").document("LA").set(City.serializer(), city, encodeDefaults = true)
db.collection("cities").document("LA").set(City.serializer(), city) { encodeDefaults = true }
```

The `encodeDefaults` parameter is optional and defaults to `true`, set this to false to omit writing optional properties if they are equal to theirs default values.
The `buildSettings` closure is optional and allows for configuring serialization behaviour.

Setting the `encodeDefaults` parameter is optional and defaults to `true`, set this to false to omit writing optional properties if they are equal to theirs default values.
Using [@EncodeDefault](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-encode-default/) on properties is a recommended way to locally override the behavior set with `encodeDefaults`.

You can also omit the serializer but this is discouraged due to a [current limitation on Kotlin/JS and Kotlin/Native](https://github.com/Kotlin/kotlinx.serialization/issues/1116#issuecomment-704342452)
You can also omit the serializer if it can be inferred using `serializer<KType>()`.
To support [contextual serialization](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization) or [open polymorphism](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism) the `serializersModule` can be overridden in the `buildSettings` closure:

```kotlin
@Serializable
abstract class AbstractCity {
abstract val name: String
}

@Serializable
@SerialName("capital")
data class Capital(override val name: String, val isSeatOfGovernment: Boolean) : AbstractCity()

val module = SerializersModule {
polymorphic(AbstractCity::class, AbstractCity.serializer()) {
subclass(Capital::class, Capital.serializer())
}
}

val city = Capital("London", true)
db.collection("cities").document("UK").set(AbstractCity.serializer(), city) {
encodeDefaults = true
serializersModule = module

}
val storedCity = db.collection("cities").document("UK").get().data(AbstractCity.serializer()) {
serializersModule = module
}
```

<h4><a href="https://firebase.google.com/docs/firestore/manage-data/add-data#server_timestamp">Server Timestamp</a></h3>

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ subprojects {
"commonTestImplementation"(kotlin("test-common"))
"commonTestImplementation"(kotlin("test-annotations-common"))
if (this@afterEvaluate.name != "firebase-crashlytics") {
"jvmMainApi"("dev.gitlive:firebase-java-sdk:0.3.0")
"jvmMainApi"("dev.gitlive:firebase-java-sdk:0.4.0")
"jvmMainApi"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion") {
exclude("com.google.android.gms")
}
Expand Down
4 changes: 2 additions & 2 deletions firebase-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gitlive/firebase-app",
"version": "1.11.0",
"version": "1.11.1",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-app.js",
"scripts": {
Expand All @@ -23,7 +23,7 @@
},
"homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk",
"dependencies": {
"@gitlive/firebase-common": "1.11.0",
"@gitlive/firebase-common": "1.11.1",
"firebase": "9.19.1",
"kotlin": "1.8.20",
"kotlinx-coroutines-core": "1.6.4"
Expand Down
4 changes: 2 additions & 2 deletions firebase-auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gitlive/firebase-auth",
"version": "1.11.0",
"version": "1.12.0",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-auth.js",
"scripts": {
Expand All @@ -23,7 +23,7 @@
},
"homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk",
"dependencies": {
"@gitlive/firebase-app": "1.11.0",
"@gitlive/firebase-app": "1.12.0",
"firebase": "9.19.1",
"kotlin": "1.8.20",
"kotlinx-coroutines-core": "1.6.4"
Expand Down
2 changes: 1 addition & 1 deletion firebase-common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gitlive/firebase-common",
"version": "1.11.0",
"version": "1.11.1",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-common.js",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,41 @@

package dev.gitlive.firebase

import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.CompositeDecoder

actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor): CompositeDecoder = when(descriptor.kind) {
StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map ->
FirebaseClassDecoder(map.size, { map.containsKey(it) }) { desc, index ->
val elementName = desc.getElementName(index)
if (desc.kind is PolymorphicKind && elementName == "value") {
map
} else {
map[desc.getElementName(index)]
}
}
}
StructureKind.LIST ->
when(value) {
is List<*> -> value
is Map<*, *> -> value.asSequence()
.sortedBy { (it) -> it.toString().toIntOrNull() }
.map { (_, it) -> it }
.toList()
else -> error("unexpected type, got $value when expecting a list")
}
.let { FirebaseCompositeDecoder(it.size) { _, index -> it[index] } }
StructureKind.MAP -> (value as Map<*, *>).entries.toList().let {
FirebaseCompositeDecoder(it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } }
}
else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet")
actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) {
StructureKind.CLASS, StructureKind.OBJECT -> decodeAsMap(false)
StructureKind.LIST -> (value as? List<*>).orEmpty().let {
FirebaseCompositeDecoder(it.size, settings) { _, index -> it[index] }
}

StructureKind.MAP -> (value as? Map<*, *>).orEmpty().entries.toList().let {
FirebaseCompositeDecoder(
it.size,
settings
) { _, index -> it[index / 2].run { if (index % 2 == 0) key else value } }
}

is PolymorphicKind -> decodeAsMap(polymorphicIsNested)
else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet")
}

actual fun getPolymorphicType(value: Any?, discriminator: String): String =
(value as Map<*,*>)[discriminator] as String
(value as? Map<*,*>).orEmpty()[discriminator] as String

private fun FirebaseDecoder.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as? Map<*, *>).orEmpty().let { map ->
FirebaseClassDecoder(map.size, settings, { map.containsKey(it) }) { desc, index ->
if (isNestedPolymorphic) {
if (desc.getElementName(index) == "value")
map
else {
map[desc.getElementName(index)]
}
} else {
map[desc.getElementName(index)]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@

package dev.gitlive.firebase

import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlin.collections.set

actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) {
StructureKind.LIST -> mutableListOf<Any?>()
.also { value = it }
.let { FirebaseCompositeEncoder(shouldEncodeElementDefault) { _, index, value -> it.add(index, value) } }
.let { FirebaseCompositeEncoder(settings) { _, index, value -> it.add(index, value) } }
StructureKind.MAP -> mutableListOf<Any?>()
.let { FirebaseCompositeEncoder(shouldEncodeElementDefault, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } }
StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf<Any?, Any?>()
.also { value = it }
.let { FirebaseCompositeEncoder(shouldEncodeElementDefault,
.let { FirebaseCompositeEncoder(settings, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } }
StructureKind.CLASS, StructureKind.OBJECT -> encodeAsMap(descriptor)
is PolymorphicKind -> encodeAsMap(descriptor)
else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet")
}

private fun FirebaseEncoder.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = mutableMapOf<Any?, Any?>()
.also { value = it }
.let {
FirebaseCompositeEncoder(
settings,
setPolymorphicType = { discriminator, type ->
it[discriminator] = type
},
set = { _, index, value -> it[descriptor.getElementName(index)] = value }
) }
else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet")
}
)
}
Loading

0 comments on commit e316f61

Please sign in to comment.