From 44ab9e1dddda282014e2ce1e301089058ad7f466 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Fri, 19 Nov 2021 09:17:42 -0700 Subject: [PATCH 01/13] Added workaround for XCode 13 compatibility --- firebase-app/build.gradle.kts | 2 +- firebase-auth/build.gradle.kts | 2 +- firebase-config/build.gradle.kts | 2 +- firebase-database/build.gradle.kts | 2 +- firebase-firestore/build.gradle.kts | 2 +- firebase-functions/build.gradle.kts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firebase-app/build.gradle.kts b/firebase-app/build.gradle.kts index eff011173..7c80be31a 100644 --- a/firebase-app/build.gradle.kts +++ b/firebase-app/build.gradle.kts @@ -90,7 +90,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseCore") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } diff --git a/firebase-auth/build.gradle.kts b/firebase-auth/build.gradle.kts index 00c0a4be8..87c5ef92e 100644 --- a/firebase-auth/build.gradle.kts +++ b/firebase-auth/build.gradle.kts @@ -119,7 +119,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseAuth") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } diff --git a/firebase-config/build.gradle.kts b/firebase-config/build.gradle.kts index 6af6ec0ea..938c48f4e 100644 --- a/firebase-config/build.gradle.kts +++ b/firebase-config/build.gradle.kts @@ -104,7 +104,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseRemoteConfig") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 8b5c08b7d..756b0c277 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -95,7 +95,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseDatabase") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } diff --git a/firebase-firestore/build.gradle.kts b/firebase-firestore/build.gradle.kts index bff60b38e..6cc4e2af9 100644 --- a/firebase-firestore/build.gradle.kts +++ b/firebase-firestore/build.gradle.kts @@ -102,7 +102,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseFirestore") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } diff --git a/firebase-functions/build.gradle.kts b/firebase-functions/build.gradle.kts index f9e42f1ce..38655c31c 100644 --- a/firebase-functions/build.gradle.kts +++ b/firebase-functions/build.gradle.kts @@ -90,7 +90,7 @@ kotlin { compilations.getByName("main") { cinterops.create("FirebaseFunctions") { compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + extraOpts("-verbose","-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=") } } } From c1073426818db8100c915952b2a39662ea4bf12d Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Fri, 19 Nov 2021 16:03:41 -0700 Subject: [PATCH 02/13] Added support for writing Timestamps on Android --- build.gradle.kts | 2 +- firebase-common/build.gradle.kts | 1 + .../kotlin/dev/gitlive/firebase/_encoders.kt | 33 +++++++-- .../kotlin/dev/gitlive/firebase/_types.kt | 5 ++ .../kotlin/dev/gitlive/firebase/encoders.kt | 70 ++++++++++++++++--- .../kotlin/dev/gitlive/firebase/types.kt | 54 ++++++++++++++ .../kotlin/dev/gitlive/firebase/_types.kt | 5 ++ .../kotlin/dev/gitlive/firebase/_types.kt | 5 ++ .../gitlive/firebase/firestore/firestore.kt | 2 +- 9 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt create mode 100644 firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt create mode 100644 firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt create mode 100644 firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt diff --git a/build.gradle.kts b/build.gradle.kts index e512e1f2a..20e08f8ee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,7 @@ tasks { subprojects { - group = "dev.gitlive" + group = "dev.litclimbing" apply(plugin="com.adarshr.test-logger") diff --git a/firebase-common/build.gradle.kts b/firebase-common/build.gradle.kts index ba4c667cf..aca46d503 100644 --- a/firebase-common/build.gradle.kts +++ b/firebase-common/build.gradle.kts @@ -93,6 +93,7 @@ kotlin { val androidMain by getting { dependencies { api("com.google.firebase:firebase-common-ktx") + api("com.google.firebase:firebase-firestore-ktx") } } diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index 84576846e..d22cb66d7 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,18 +4,41 @@ package dev.gitlive.firebase -import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.encoding.CompositeEncoder import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when (descriptor.kind as StructureKind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } StructureKind.MAP -> mutableListOf() - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf() + .let { + FirebaseCompositeEncoder( + shouldEncodeElementDefault, + positiveInfinity, + { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } + } + StructureKind.CLASS -> mutableMapOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { + FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> + it[descriptor.getElementName(index)] = value + } + } + StructureKind.OBJECT -> { + when (descriptor.serialName) { + "firebaseTimestamp" -> FirebaseTimestampCompositeEncoder { value = it } + else -> mutableMapOf() + .also { value = it } + .let { + FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> + it[descriptor.getElementName( + index + )] = value + } + } + } + } } \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt new file mode 100644 index 000000000..048e8c7d5 --- /dev/null +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt @@ -0,0 +1,5 @@ +package dev.gitlive.firebase + +actual fun Timestamp.asNative() : Any { + return com.google.firebase.Timestamp(seconds, nanoseconds) +} \ No newline at end of file diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt index afb637492..5a96f669f 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt @@ -4,13 +4,26 @@ package dev.gitlive.firebase -import kotlinx.serialization.* -import kotlinx.serialization.encoding.* -import kotlinx.serialization.descriptors.* +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.CompositeEncoder +import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.modules.EmptySerializersModule - -fun encode(strategy: SerializationStrategy, value: T, shouldEncodeElementDefault: Boolean, positiveInfinity: Any = Double.POSITIVE_INFINITY): Any? = - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { encodeSerializableValue(strategy, value) }.value//.also { println("encoded $it") } +import kotlinx.serialization.modules.SerializersModule + +fun encode( + strategy: SerializationStrategy, + value: T, + shouldEncodeElementDefault: Boolean, + positiveInfinity: Any = Double.POSITIVE_INFINITY +): Any? = + FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { + encodeSerializableValue( + strategy, + value + ) + }.value//.also { println("encoded $it") } inline fun encode(value: T, shouldEncodeElementDefault: Boolean, positiveInfinity: Any = Double.POSITIVE_INFINITY): Any? = value?.let { FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { encodeSerializableValue(it.firebaseSerializer(), it) }.value @@ -79,18 +92,59 @@ class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positive } abstract class TimestampEncoder(internal val positiveInfinity: Any) { - fun encodeTimestamp(value: Double) = when(value) { + fun encodeTimestamp(value: Double) = when (value) { Double.POSITIVE_INFINITY -> positiveInfinity else -> value } } +class FirebaseTimestampCompositeEncoder(val onSet: (Any)->Unit) : CompositeEncoder { + var nanos: Int = 0 + var seconds: Long = 0 + + override val serializersModule = EmptySerializersModule + + override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) = throw IllegalStateException() + override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) = throw IllegalStateException() + override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) = throw IllegalStateException() + override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) = throw IllegalStateException() + override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) = throw IllegalStateException() + @ExperimentalSerializationApi + override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder = throw IllegalStateException() + + override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) { + nanos = value + } + + override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) { + seconds = value + } + + @ExperimentalSerializationApi + override fun encodeNullableSerializableElement( + descriptor: SerialDescriptor, + index: Int, + serializer: SerializationStrategy, + value: T? + ) = throw IllegalStateException() + + override fun encodeSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy, value: T) = throw IllegalStateException() + + override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short) = throw IllegalStateException() + + override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) = throw IllegalStateException() + + override fun endStructure(descriptor: SerialDescriptor) { + onSet(Timestamp(nanos, seconds).asNative()) + } +} + open class FirebaseCompositeEncoder constructor( private val shouldEncodeElementDefault: Boolean, positiveInfinity: Any, private val end: () -> Unit = {}, private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit -): TimestampEncoder(positiveInfinity), CompositeEncoder { +) : TimestampEncoder(positiveInfinity), CompositeEncoder { override val serializersModule = EmptySerializersModule diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt new file mode 100644 index 000000000..d6b19a304 --- /dev/null +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt @@ -0,0 +1,54 @@ +package dev.gitlive.firebase + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable(with = FirebaseTimestampSerializer::class) +class Timestamp( + val nanoseconds: Int, + val seconds: Long +) { + fun test(){} +} + +expect fun Timestamp.asNative() : Any + +private object TimestampDescriptor : SerialDescriptor { + override val kind = StructureKind.OBJECT + override val serialName = "firebaseTimestamp" + override val elementsCount get() = 2 + override fun getElementIndex(name: String) = when (name) { + "nanos" -> 0 + "seconds" -> 1 + else -> -1 + } + override fun getElementName(index: Int) = when (index) { + 0 -> "nanos" + 1 -> "seconds" + else -> throw IndexOutOfBoundsException() + } + override fun getElementAnnotations(index: Int) = emptyList() + override fun getElementDescriptor(index: Int) = throw NotImplementedError() + override fun isElementOptional(index: Int) = false +} + +private object FirebaseTimestampSerializer : KSerializer { + override val descriptor = TimestampDescriptor + + override fun serialize(encoder: Encoder, value: Timestamp) { + println("Serializing timestamp") + val collection = encoder.beginCollection(descriptor, 2) + collection.encodeIntElement(descriptor, 0, value.nanoseconds) + collection.encodeLongElement(descriptor, 1, value.seconds) + collection.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): Timestamp { + throw NotImplementedError() + } +} \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt new file mode 100644 index 000000000..25f4c7d89 --- /dev/null +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt @@ -0,0 +1,5 @@ +package dev.gitlive.firebase + +actual fun Timestamp.asNative() : Any { + TODO() +} \ No newline at end of file diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt new file mode 100644 index 000000000..25f4c7d89 --- /dev/null +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt @@ -0,0 +1,5 @@ +package dev.gitlive.firebase + +actual fun Timestamp.asNative() : Any { + TODO() +} \ No newline at end of file diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 115d4a114..e1776762f 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -450,4 +450,4 @@ actual object FieldValue { actual fun arrayUnion(vararg elements: Any): Any = FieldValue.arrayUnion(*elements) actual fun arrayRemove(vararg elements: Any): Any = FieldValue.arrayRemove(*elements) actual fun delete(): Any = delete -} +} \ No newline at end of file From 36a37c59880a0fc4a6bb498e645e274dea780a7b Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sat, 20 Nov 2021 09:38:55 -0700 Subject: [PATCH 03/13] Working Timestamps and serverTimestamps on Android --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 8 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 27 +-- .../kotlin/dev/gitlive/firebase/_types.kt | 5 - .../kotlin/dev/gitlive/firebase/decoders.kt | 35 ++-- .../kotlin/dev/gitlive/firebase/encoders.kt | 77 ++------- .../dev/gitlive/firebase/serializers.kt | 72 ++++++-- .../kotlin/dev/gitlive/firebase/types.kt | 54 ------ .../kotlin/dev/gitlive/firebase/_decoders.kt | 8 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 6 +- .../kotlin/dev/gitlive/firebase/_types.kt | 5 - .../kotlin/dev/gitlive/firebase/_decoders.kt | 8 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 6 +- .../kotlin/dev/gitlive/firebase/_types.kt | 5 - .../dev/gitlive/firebase/database/database.kt | 12 +- .../dev/gitlive/firebase/database/database.kt | 12 +- .../dev/gitlive/firebase/database/database.kt | 8 - firebase-firestore/build.gradle.kts | 1 + .../gitlive/firebase/firestore/firestore.kt | 154 ++++++++++++------ .../gitlive/firebase/firestore/firestore.kt | 32 +++- .../gitlive/firebase/firestore/firestore.kt | 53 ++++-- .../gitlive/firebase/firestore/firestore.kt | 54 ++++-- 21 files changed, 342 insertions(+), 300 deletions(-) delete mode 100644 firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt delete mode 100644 firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt delete mode 100644 firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt delete mode 100644 firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index 2b31cece7..cca56e434 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -10,14 +10,14 @@ import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor): CompositeDecoder = when(descriptor.kind as StructureKind) { StructureKind.CLASS, StructureKind.OBJECT -> (value as Map<*, *>).let { map -> - FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } + FirebaseClassDecoder(map.size, { map.containsKey(it) }, {v -> getDecoder(v)}) { desc, index -> map[desc.getElementName(index)] } } StructureKind.LIST -> (value as List<*>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index] } } StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } } \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index d22cb66d7..dc4aeb982 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -12,33 +12,22 @@ import kotlin.collections.set actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when (descriptor.kind as StructureKind) { StructureKind.LIST -> mutableListOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> it.add(index, value) } } StructureKind.MAP -> mutableListOf() .let { FirebaseCompositeEncoder( shouldEncodeElementDefault, - positiveInfinity, + {getEncoder(shouldEncodeElementDefault)}, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS -> mutableMapOf() + StructureKind.OBJECT, StructureKind.CLASS -> mutableMapOf() .also { value = it } .let { - FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> - it[descriptor.getElementName(index)] = value + FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> + it[descriptor.getElementName( + index + )] = value } } - StructureKind.OBJECT -> { - when (descriptor.serialName) { - "firebaseTimestamp" -> FirebaseTimestampCompositeEncoder { value = it } - else -> mutableMapOf() - .also { value = it } - .let { - FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> - it[descriptor.getElementName( - index - )] = value - } - } - } - } + } \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt deleted file mode 100644 index 048e8c7d5..000000000 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_types.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.gitlive.firebase - -actual fun Timestamp.asNative() : Any { - return com.google.firebase.Timestamp(seconds, nanoseconds) -} \ No newline at end of file diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt index c771d63e1..6f6e5df10 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt @@ -16,28 +16,29 @@ import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.serializer @Suppress("UNCHECKED_CAST") -inline fun decode(value: Any?, noinline decodeDouble: (value: Any?) -> Double? = { null }): T { +inline fun decode(value: Any?): T { val strategy = serializer() - return decode(strategy as DeserializationStrategy, value, decodeDouble) + return decode(strategy as DeserializationStrategy, value) } -fun decode(strategy: DeserializationStrategy, value: Any?, decodeDouble: (value: Any?) -> Double? = { null }): T { +fun decode(strategy: DeserializationStrategy, value: Any?): T { require(value != null || strategy.descriptor.isNullable) { "Value was null for non-nullable type ${strategy.descriptor.serialName}" } - return FirebaseDecoder(value, decodeDouble).decodeSerializableValue(strategy) + return FirebaseDecoder(value).decodeSerializableValue(strategy) } -expect fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder +expect fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor): CompositeDecoder -class FirebaseDecoder(internal val value: Any?, private val decodeDouble: (value: Any?) -> Double?) : Decoder { +open class FirebaseDecoder(internal val value: Any?) : Decoder { + open fun getDecoder(value: Any?) : FirebaseDecoder = FirebaseDecoder(value) override val serializersModule: SerializersModule get() = EmptySerializersModule - override fun beginStructure(descriptor: SerialDescriptor) = structureDecoder(descriptor, decodeDouble) + override fun beginStructure(descriptor: SerialDescriptor) = structureDecoder(descriptor) override fun decodeString() = decodeString(value) - override fun decodeDouble() = decodeDouble(value, decodeDouble) + override fun decodeDouble() = decodeDouble(value) override fun decodeLong() = decodeLong(value) @@ -60,15 +61,15 @@ class FirebaseDecoder(internal val value: Any?, private val decodeDouble: (value override fun decodeNull() = decodeNull(value) @ExperimentalSerializationApi - override fun decodeInline(inlineDescriptor: SerialDescriptor) = FirebaseDecoder(value, decodeDouble) + override fun decodeInline(inlineDescriptor: SerialDescriptor) = FirebaseDecoder(value) } class FirebaseClassDecoder( - decodeDouble: (value: Any?) -> Double?, size: Int, private val containsKey: (name: String) -> Boolean, + getDecoder: (Any?)->FirebaseDecoder, get: (descriptor: SerialDescriptor, index: Int) -> Any? -) : FirebaseCompositeDecoder(decodeDouble, size, get) { +) : FirebaseCompositeDecoder(size, getDecoder, get) { private var index: Int = 0 override fun decodeSequentially() = false @@ -81,8 +82,8 @@ class FirebaseClassDecoder( } open class FirebaseCompositeDecoder constructor( - private val decodeDouble: (value: Any?) -> Double?, private val size: Int, + private val getDecoder: (Any?)->FirebaseDecoder, private val get: (descriptor: SerialDescriptor, index: Int) -> Any? ): CompositeDecoder { @@ -99,7 +100,7 @@ open class FirebaseCompositeDecoder constructor( index: Int, deserializer: DeserializationStrategy, previousValue: T? - ) = deserializer.deserialize(FirebaseDecoder(get(descriptor, index), decodeDouble)) + ) = deserializer.deserialize(getDecoder(get(descriptor, index))) override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int) = decodeBoolean(get(descriptor, index)) @@ -107,7 +108,7 @@ open class FirebaseCompositeDecoder constructor( override fun decodeCharElement(descriptor: SerialDescriptor, index: Int) = decodeChar(get(descriptor, index)) - override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int) = decodeDouble(get(descriptor, index), decodeDouble) + override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int) = decodeDouble(get(descriptor, index)) override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int) = decodeFloat(get(descriptor, index)) @@ -133,15 +134,15 @@ open class FirebaseCompositeDecoder constructor( @ExperimentalSerializationApi override fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder = - FirebaseDecoder(get(descriptor, index), decodeDouble) + getDecoder(get(descriptor, index)) } private fun decodeString(value: Any?) = value.toString() -private fun decodeDouble(value: Any?, decodeDouble: (value: Any?) -> Double?) = when(value) { +private fun decodeDouble(value: Any?) = when(value) { is Number -> value.toDouble() is String -> value.toDouble() - else -> decodeDouble(value) ?: throw SerializationException("Expected $value to be double") + else -> throw SerializationException("Expected $value to be double") } private fun decodeLong(value: Any?) = when(value) { diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt index 5a96f669f..a70bf44d2 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt @@ -15,23 +15,24 @@ import kotlinx.serialization.modules.SerializersModule fun encode( strategy: SerializationStrategy, value: T, - shouldEncodeElementDefault: Boolean, - positiveInfinity: Any = Double.POSITIVE_INFINITY + shouldEncodeElementDefault: Boolean ): Any? = - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { + FirebaseEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue( strategy, value ) }.value//.also { println("encoded $it") } -inline fun encode(value: T, shouldEncodeElementDefault: Boolean, positiveInfinity: Any = Double.POSITIVE_INFINITY): Any? = value?.let { - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { encodeSerializableValue(it.firebaseSerializer(), it) }.value +@Suppress("UNCHECKED_CAST") +inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = value?.let { + FirebaseEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(firebaseSerializer(it) as SerializationStrategy, it) }.value } expect fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder -class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positiveInfinity: Any) : TimestampEncoder(positiveInfinity), Encoder { +open class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean) : Encoder { + open fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirebaseEncoder(shouldEncodeElementDefault) var value: Any? = null @@ -51,7 +52,7 @@ class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positive } override fun encodeDouble(value: Double) { - this.value = encodeTimestamp(value) + this.value = value } override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) { @@ -88,63 +89,15 @@ class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positive @ExperimentalSerializationApi override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder = - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity) -} - -abstract class TimestampEncoder(internal val positiveInfinity: Any) { - fun encodeTimestamp(value: Double) = when (value) { - Double.POSITIVE_INFINITY -> positiveInfinity - else -> value - } -} - -class FirebaseTimestampCompositeEncoder(val onSet: (Any)->Unit) : CompositeEncoder { - var nanos: Int = 0 - var seconds: Long = 0 - - override val serializersModule = EmptySerializersModule - - override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) = throw IllegalStateException() - override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) = throw IllegalStateException() - override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) = throw IllegalStateException() - override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) = throw IllegalStateException() - override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) = throw IllegalStateException() - @ExperimentalSerializationApi - override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder = throw IllegalStateException() - - override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) { - nanos = value - } - - override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) { - seconds = value - } - - @ExperimentalSerializationApi - override fun encodeNullableSerializableElement( - descriptor: SerialDescriptor, - index: Int, - serializer: SerializationStrategy, - value: T? - ) = throw IllegalStateException() - - override fun encodeSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy, value: T) = throw IllegalStateException() - - override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short) = throw IllegalStateException() - - override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) = throw IllegalStateException() - - override fun endStructure(descriptor: SerialDescriptor) { - onSet(Timestamp(nanos, seconds).asNative()) - } + getEncoder(shouldEncodeElementDefault) } open class FirebaseCompositeEncoder constructor( private val shouldEncodeElementDefault: Boolean, - positiveInfinity: Any, + private val getEncoder: (Boolean)->FirebaseEncoder, private val end: () -> Unit = {}, private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit -) : TimestampEncoder(positiveInfinity), CompositeEncoder { +) : CompositeEncoder { override val serializersModule = EmptySerializersModule @@ -167,7 +120,7 @@ open class FirebaseCompositeEncoder constructor( descriptor, index, value?.let { - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { + getEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(serializer, value) }.value } @@ -181,7 +134,7 @@ open class FirebaseCompositeEncoder constructor( ) = set( descriptor, index, - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { + getEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(serializer, value) }.value ) @@ -192,7 +145,7 @@ open class FirebaseCompositeEncoder constructor( override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) = set(descriptor, index, value) - override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) = set(descriptor, index, encodeTimestamp(value)) + override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) = set(descriptor, index, value) override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) = set(descriptor, index, value) @@ -206,7 +159,7 @@ open class FirebaseCompositeEncoder constructor( @ExperimentalSerializationApi override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder = - FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity) + FirebaseEncoder(shouldEncodeElementDefault) } diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt index 43461fa15..5572e4ac5 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt @@ -9,18 +9,15 @@ import kotlinx.serialization.encoding.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.builtins.serializer -@Suppress("UNCHECKED_CAST") -inline fun T.firebaseSerializer() = runCatching { serializer() } - .getOrElse { - when(this) { - is Map<*, *> -> FirebaseMapSerializer() - is List<*> -> FirebaseListSerializer() - is Set<*> -> FirebaseListSerializer() - else -> this::class.serializer() - } as SerializationStrategy +fun firebaseSerializer(it: Any): SerializationStrategy<*> = + when(it) { + is Map<*, *> -> FirebaseMapSerializer(::firebaseSerializer) + is List<*> -> FirebaseListSerializer(::firebaseSerializer) + is Set<*> -> FirebaseListSerializer(::firebaseSerializer) + else -> it::class.serializer() } -class FirebaseMapSerializer : KSerializer> { +class FirebaseMapSerializer(val getSerializer: (Any)->SerializationStrategy<*>) : KSerializer> { lateinit var keys: List lateinit var map: Map @@ -43,7 +40,7 @@ class FirebaseMapSerializer : KSerializer> { val collectionEncoder = encoder.beginCollection(descriptor, value.size) keys.forEachIndexed { index, key -> val listValue = map.getValue(key) - val serializer = (listValue?.firebaseSerializer() ?: Unit.serializer()) as KSerializer + val serializer = (if (listValue!=null) getSerializer(listValue) else Unit.serializer()) as KSerializer String.serializer().let { collectionEncoder.encodeSerializableElement(it.descriptor, index * 2, it, key) } @@ -65,7 +62,7 @@ class FirebaseMapSerializer : KSerializer> { } } -class FirebaseListSerializer : KSerializer> { +class FirebaseListSerializer(val getSerializer: (Any)->SerializationStrategy<*>) : KSerializer> { lateinit var list: List @@ -85,7 +82,7 @@ class FirebaseListSerializer : KSerializer> { list = value.toList() val collectionEncoder = encoder.beginCollection(descriptor, list.size) list.forEachIndexed { index, listValue -> - val serializer = (listValue?.firebaseSerializer() ?: Unit.serializer()) as KSerializer + val serializer = (if (listValue!=null) getSerializer(listValue) else Unit.serializer()) as KSerializer collectionEncoder.encodeNullableSerializableElement( serializer.descriptor, index, serializer, listValue ) @@ -104,3 +101,52 @@ class FirebaseListSerializer : KSerializer> { } } +/*** Used for built-in types that don't actually need a serializer. */ +object DummySerializer : KSerializer { + override fun deserialize(decoder: Decoder): Any { + throw NotImplementedError() + } + + override val descriptor: SerialDescriptor = object : SerialDescriptor { + @ExperimentalSerializationApi + override val elementsCount: Int + get() = throw NotImplementedError() + + @ExperimentalSerializationApi + override val kind: SerialKind + get() = throw NotImplementedError() + + @ExperimentalSerializationApi + override val serialName: String + get() = throw NotImplementedError() + + @ExperimentalSerializationApi + override fun getElementAnnotations(index: Int): List { + throw NotImplementedError() + } + + @ExperimentalSerializationApi + override fun getElementDescriptor(index: Int): SerialDescriptor { + throw NotImplementedError() + } + + @ExperimentalSerializationApi + override fun getElementIndex(name: String): Int { + throw NotImplementedError() + } + + @ExperimentalSerializationApi + override fun getElementName(index: Int): String { + throw NotImplementedError() + } + + @ExperimentalSerializationApi + override fun isElementOptional(index: Int): Boolean { + throw NotImplementedError() + } + } + + override fun serialize(encoder: Encoder, value: Any) { + throw NotImplementedError() + } +} \ No newline at end of file diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt deleted file mode 100644 index d6b19a304..000000000 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/types.kt +++ /dev/null @@ -1,54 +0,0 @@ -package dev.gitlive.firebase - -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.StructureKind -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder - -@Serializable(with = FirebaseTimestampSerializer::class) -class Timestamp( - val nanoseconds: Int, - val seconds: Long -) { - fun test(){} -} - -expect fun Timestamp.asNative() : Any - -private object TimestampDescriptor : SerialDescriptor { - override val kind = StructureKind.OBJECT - override val serialName = "firebaseTimestamp" - override val elementsCount get() = 2 - override fun getElementIndex(name: String) = when (name) { - "nanos" -> 0 - "seconds" -> 1 - else -> -1 - } - override fun getElementName(index: Int) = when (index) { - 0 -> "nanos" - 1 -> "seconds" - else -> throw IndexOutOfBoundsException() - } - override fun getElementAnnotations(index: Int) = emptyList() - override fun getElementDescriptor(index: Int) = throw NotImplementedError() - override fun isElementOptional(index: Int) = false -} - -private object FirebaseTimestampSerializer : KSerializer { - override val descriptor = TimestampDescriptor - - override fun serialize(encoder: Encoder, value: Timestamp) { - println("Serializing timestamp") - val collection = encoder.beginCollection(descriptor, 2) - collection.encodeIntElement(descriptor, 0, value.nanoseconds) - collection.encodeLongElement(descriptor, 1, value.seconds) - collection.endStructure(descriptor) - } - - override fun deserialize(decoder: Decoder): Timestamp { - throw NotImplementedError() - } -} \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt index 05ffad2cd..552091130 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -10,14 +10,14 @@ import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor): CompositeDecoder = when(descriptor.kind as StructureKind) { StructureKind.CLASS, StructureKind.OBJECT -> (value as Map<*, *>).let { map -> - FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } + FirebaseClassDecoder(map.size, { map.containsKey(it) }, {v -> getDecoder(v)}) { desc, index -> map[desc.getElementName(index)] } } StructureKind.LIST -> (value as List<*>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index] } } StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } } \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt index 84576846e..3d4bdc865 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -12,10 +12,10 @@ import kotlin.collections.set actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { StructureKind.LIST -> mutableListOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> it.add(index, value) } } StructureKind.MAP -> mutableListOf() - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> it[descriptor.getElementName(index)] = value } } } \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt deleted file mode 100644 index 25f4c7d89..000000000 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_types.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.gitlive.firebase - -actual fun Timestamp.asNative() : Any { - TODO() -} \ No newline at end of file diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt index 4e0d0b226..665d9b2c3 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -12,16 +12,16 @@ import kotlinx.serialization.descriptors.StructureKind import kotlin.js.Json @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor): CompositeDecoder = when(descriptor.kind as StructureKind) { StructureKind.CLASS, StructureKind.OBJECT -> (value as Json).let { json -> - FirebaseClassDecoder(decodeDouble, js("Object").keys(value).length as Int, { json[it] != undefined }) { + FirebaseClassDecoder(js("Object").keys(value).length as Int, { json[it] != undefined }, {v -> getDecoder(v)}) { desc, index -> json[desc.getElementName(index)] } } StructureKind.LIST -> (value as Array<*>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index] } } StructureKind.MAP -> (js("Object").entries(value) as Array>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) get(0) else get(1) } } + FirebaseCompositeDecoder(it.size, {v -> getDecoder(v)}) { _, index -> it[index/2].run { if(index % 2 == 0) get(0) else get(1) } } } } diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt index 5a7ce7a0f..33e31e3dd 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -12,15 +12,15 @@ import kotlin.js.json actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { StructureKind.LIST -> Array(descriptor.elementsCount) { null } .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[index] = value } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> it[index] = value } } StructureKind.MAP -> { val map = json() var lastKey: String = "" value = map - FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> if(index % 2 == 0) lastKey = value as String else map[lastKey] = value } + FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> if(index % 2 == 0) lastKey = value as String else map[lastKey] = value } } StructureKind.CLASS, StructureKind.OBJECT -> json() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, {getEncoder(shouldEncodeElementDefault)}) { _, index, value -> it[descriptor.getElementName(index)] = value } } } diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt deleted file mode 100644 index 25f4c7d89..000000000 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_types.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.gitlive.firebase - -actual fun Timestamp.asNative() : Any { - TODO() -} \ No newline at end of file diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index e92f95dd3..b2b9c5bb5 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -9,11 +9,8 @@ import com.google.firebase.database.ChildEventListener import com.google.firebase.database.Logger import com.google.firebase.database.ServerValue import com.google.firebase.database.ValueEventListener -import dev.gitlive.firebase.Firebase -import dev.gitlive.firebase.FirebaseApp +import dev.gitlive.firebase.* import dev.gitlive.firebase.database.ChildEvent.Type -import dev.gitlive.firebase.decode -import dev.gitlive.firebase.safeOffer import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope @@ -27,13 +24,6 @@ import kotlinx.coroutines.tasks.await import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - dev.gitlive.firebase.encode(value, shouldEncodeElementDefault, ServerValue.TIMESTAMP) - -internal fun encode(strategy: SerializationStrategy , value: T, shouldEncodeElementDefault: Boolean): Any? = - dev.gitlive.firebase.encode(strategy, value, shouldEncodeElementDefault, ServerValue.TIMESTAMP) - suspend fun Task.awaitWhileOnline(): T = coroutineScope { val notConnected = Firebase.database diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index 9ba833dc6..80186b3d6 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -6,12 +6,9 @@ package dev.gitlive.firebase.database import cocoapods.FirebaseDatabase.* import cocoapods.FirebaseDatabase.FIRDataEventType.* -import dev.gitlive.firebase.Firebase -import dev.gitlive.firebase.FirebaseApp +import dev.gitlive.firebase.* import dev.gitlive.firebase.database.ChildEvent.Type import dev.gitlive.firebase.database.ChildEvent.Type.* -import dev.gitlive.firebase.decode -import dev.gitlive.firebase.safeOffer import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.ClosedSendChannelException @@ -27,13 +24,6 @@ import platform.Foundation.* import kotlin.collections.component1 import kotlin.collections.component2 -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - dev.gitlive.firebase.encode(value, shouldEncodeElementDefault, FIRServerValue.timestamp()) - -internal fun encode(strategy: SerializationStrategy , value: T, shouldEncodeElementDefault: Boolean): Any? = - dev.gitlive.firebase.encode(strategy, value, shouldEncodeElementDefault, FIRServerValue.timestamp()) - actual val Firebase.database by lazy { FirebaseDatabase(FIRDatabase.database()) } diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index 9fbc04bba..92b3dd301 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -15,14 +15,6 @@ import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy import kotlin.js.Promise -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - encode(value, shouldEncodeElementDefault, firebase.database.ServerValue.TIMESTAMP) - -internal fun encode(strategy: SerializationStrategy, value: T, shouldEncodeElementDefault: Boolean): Any? = - encode(strategy, value, shouldEncodeElementDefault, firebase.database.ServerValue.TIMESTAMP) - - actual val Firebase.database get() = rethrow { dev.gitlive.firebase.database; FirebaseDatabase(firebase.database()) } diff --git a/firebase-firestore/build.gradle.kts b/firebase-firestore/build.gradle.kts index 6cc4e2af9..a72608d53 100644 --- a/firebase-firestore/build.gradle.kts +++ b/firebase-firestore/build.gradle.kts @@ -137,6 +137,7 @@ kotlin { progressiveMode = true optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") optIn("kotlinx.serialization.InternalSerializationApi") + optIn("kotlinx.serialization.ExperimentalSerializationApi") } } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index e1776762f..f91a0f65d 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -3,9 +3,9 @@ */ @file:JvmName("android") + package dev.gitlive.firebase.firestore -import com.google.firebase.Timestamp import com.google.firebase.firestore.FieldValue import com.google.firebase.firestore.SetOptions import dev.gitlive.firebase.* @@ -15,23 +15,49 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.serializer + +actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FieldValue, is Timestamp -> DummySerializer + else -> value::class.serializer() + } -@PublishedApi -internal inline fun decode(value: Any?): T = - decode(value) { (it as? Timestamp)?.run { seconds * 1000 + (nanoseconds / 1000000.0) } } +actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = + when (value) { + is com.google.firebase.Timestamp -> DummySerializer + null -> Unit::class.serializer() + else -> value::class.serializer() + } -internal fun decode(strategy: DeserializationStrategy, value: Any?): T = - decode(strategy, value) { (it as? Timestamp)?.run { seconds * 1000 + (nanoseconds / 1000000.0) } } +actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { + override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - encode(value, shouldEncodeElementDefault, FieldValue.serverTimestamp()) + override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = + when (value) { + is Timestamp -> this.value = value.android + is FieldValue -> this.value = value + else -> super.encodeSerializableValue(serializer, value) + } +} -private fun encode(strategy: SerializationStrategy , value: T, shouldEncodeElementDefault: Boolean): Any? = - encode(strategy, value, shouldEncodeElementDefault, FieldValue.serverTimestamp()) +@Suppress("UNCHECKED_CAST") +actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDecoder(value) { + override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) -actual val Firebase.firestore get() = - FirebaseFirestore(com.google.firebase.firestore.FirebaseFirestore.getInstance()) + override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = + when (value) { + is com.google.firebase.Timestamp -> Timestamp(value.seconds, value.nanoseconds) as T + else -> super.decodeSerializableValue(deserializer) + } +} + +actual val Firebase.firestore + get() = + FirebaseFirestore(com.google.firebase.firestore.FirebaseFirestore.getInstance()) actual fun Firebase.firestore(app: FirebaseApp) = FirebaseFirestore(com.google.firebase.firestore.FirebaseFirestore.getInstance(app.android)) @@ -64,12 +90,12 @@ actual class FirebaseFirestore(val android: com.google.firebase.firestore.Fireba actual fun setSettings(persistenceEnabled: Boolean?, sslEnabled: Boolean?, host: String?, cacheSizeBytes: Long?) { android.firestoreSettings = com.google.firebase.firestore.FirebaseFirestoreSettings.Builder().also { builder -> - persistenceEnabled?.let { builder.setPersistenceEnabled(it) } - sslEnabled?.let { builder.isSslEnabled = it } - host?.let { builder.host = it } - cacheSizeBytes?.let { builder.cacheSizeBytes = it } - }.build() - } + persistenceEnabled?.let { builder.setPersistenceEnabled(it) } + sslEnabled?.let { builder.isSslEnabled = it } + host?.let { builder.host = it } + cacheSizeBytes?.let { builder.cacheSizeBytes = it } + }.build() + } actual suspend fun disableNetwork() = android.disableNetwork().await().run { } @@ -81,7 +107,7 @@ actual class FirebaseFirestore(val android: com.google.firebase.firestore.Fireba actual class WriteBatch(val android: com.google.firebase.firestore.WriteBatch) { - actual inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean, merge: Boolean) = when(merge) { + actual inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean, merge: Boolean) = when (merge) { true -> android.set(documentRef.android, encode(data, encodeDefaults)!!, SetOptions.merge()) false -> android.set(documentRef.android, encode(data, encodeDefaults)!!) }.let { this } @@ -94,16 +120,29 @@ actual class WriteBatch(val android: com.google.firebase.firestore.WriteBatch) { android.set(documentRef.android, encode(data, encodeDefaults)!!, SetOptions.mergeFieldPaths(mergeFieldPaths.map { it.android })) .let { this } - actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, merge: Boolean) = when(merge) { - true -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.merge()) - false -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!) - }.let { this } + actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, merge: Boolean) = + when (merge) { + true -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.merge()) + false -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!) + }.let { this } - actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, vararg mergeFields: String) = + actual fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean, + vararg mergeFields: String + ) = android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.mergeFields(*mergeFields)) .let { this } - actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, vararg mergeFieldPaths: FieldPath) = + actual fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean, + vararg mergeFieldPaths: FieldPath + ) = android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.mergeFieldPaths(mergeFieldPaths.map { it.android })) .let { this } @@ -148,7 +187,7 @@ actual class WriteBatch(val android: com.google.firebase.firestore.WriteBatch) { actual class Transaction(val android: com.google.firebase.firestore.Transaction) { - actual fun set(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean, merge: Boolean) = when(merge) { + actual fun set(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean, merge: Boolean) = when (merge) { true -> android.set(documentRef.android, encode(data, encodeDefaults)!!, SetOptions.merge()) false -> android.set(documentRef.android, encode(data, encodeDefaults)!!) }.let { this } @@ -167,16 +206,28 @@ actual class Transaction(val android: com.google.firebase.firestore.Transaction) data: T, encodeDefaults: Boolean, merge: Boolean - ) = when(merge) { + ) = when (merge) { true -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.merge()) false -> android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!) }.let { this } - actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, vararg mergeFields: String) = + actual fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean, + vararg mergeFields: String + ) = android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.mergeFields(*mergeFields)) .let { this } - actual fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, vararg mergeFieldPaths: FieldPath) = + actual fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean, + vararg mergeFieldPaths: FieldPath + ) = android.set(documentRef.android, encode(strategy, data, encodeDefaults)!!, SetOptions.mergeFieldPaths(mergeFieldPaths.map { it.android })) .let { this } @@ -229,7 +280,7 @@ actual class DocumentReference(val android: com.google.firebase.firestore.Docume actual fun collection(collectionPath: String) = CollectionReference(android.collection(collectionPath)) - actual suspend inline fun set(data: T, encodeDefaults: Boolean, merge: Boolean) = when(merge) { + actual suspend inline fun set(data: T, encodeDefaults: Boolean, merge: Boolean) = when (merge) { true -> android.set(encode(data, encodeDefaults)!!, SetOptions.merge()) false -> android.set(encode(data, encodeDefaults)!!) }.await().run { Unit } @@ -242,7 +293,7 @@ actual class DocumentReference(val android: com.google.firebase.firestore.Docume android.set(encode(data, encodeDefaults)!!, SetOptions.mergeFieldPaths(mergeFieldPaths.map { it.android })) .await().run { Unit } - actual suspend fun set(strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, merge: Boolean) = when(merge) { + actual suspend fun set(strategy: SerializationStrategy, data: T, encodeDefaults: Boolean, merge: Boolean) = when (merge) { true -> android.set(encode(strategy, data, encodeDefaults)!!, SetOptions.merge()) false -> android.set(encode(strategy, data, encodeDefaults)!!) }.await().run { Unit } @@ -295,13 +346,14 @@ actual class DocumentReference(val android: com.google.firebase.firestore.Docume actual suspend fun get() = DocumentSnapshot(android.get().await()) - actual val snapshots get() = callbackFlow { - val listener = android.addSnapshotListener { snapshot, exception -> - snapshot?.let { safeOffer(DocumentSnapshot(snapshot)) } - exception?.let { close(exception) } + actual val snapshots + get() = callbackFlow { + val listener = android.addSnapshotListener { snapshot, exception -> + snapshot?.let { safeOffer(DocumentSnapshot(snapshot)) } + exception?.let { close(exception) } + } + awaitClose { listener.remove() } } - awaitClose { listener.remove() } - } } actual open class Query(open val android: com.google.firebase.firestore.Query) { @@ -310,13 +362,14 @@ actual open class Query(open val android: com.google.firebase.firestore.Query) { actual fun limit(limit: Number) = Query(android.limit(limit.toLong())) - actual val snapshots get() = callbackFlow { - val listener = android.addSnapshotListener { snapshot, exception -> - snapshot?.let { safeOffer(QuerySnapshot(snapshot)) } - exception?.let { close(exception) } + actual val snapshots + get() = callbackFlow { + val listener = android.addSnapshotListener { snapshot, exception -> + snapshot?.let { safeOffer(QuerySnapshot(snapshot)) } + exception?.let { close(exception) } + } + awaitClose { listener.remove() } } - awaitClose { listener.remove() } - } internal actual fun _where(field: String, equalTo: Any?) = Query(android.whereEqualTo(field, equalTo)) internal actual fun _where(path: FieldPath, equalTo: Any?) = Query(android.whereEqualTo(path.android, equalTo)) @@ -374,6 +427,7 @@ actual class CollectionReference(override val android: com.google.firebase.fires actual suspend fun add(data: T, strategy: SerializationStrategy, encodeDefaults: Boolean) = DocumentReference(android.add(encode(strategy, data, encodeDefaults)!!).await()) + actual suspend fun add(strategy: SerializationStrategy, data: T, encodeDefaults: Boolean) = DocumentReference(android.add(encode(strategy, data, encodeDefaults)!!).await()) } @@ -409,7 +463,7 @@ actual class DocumentSnapshot(val android: com.google.firebase.firestore.Documen actual val id get() = android.id actual val reference get() = DocumentReference(android.reference) - actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = + actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = decode(value = android.getData(serverTimestampBehavior.toAndroid())) actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = @@ -441,13 +495,21 @@ actual class SnapshotMetadata(val android: com.google.firebase.firestore.Snapsho actual class FieldPath private constructor(val android: com.google.firebase.firestore.FieldPath) { actual constructor(vararg fieldNames: String) : this(com.google.firebase.firestore.FieldPath.of(*fieldNames)) + actual val documentId: FieldPath get() = FieldPath(com.google.firebase.firestore.FieldPath.documentId()) } actual object FieldValue { - actual val serverTimestamp = Double.POSITIVE_INFINITY + actual val serverTimestamp: Any get() = FieldValue.serverTimestamp() actual val delete: Any get() = FieldValue.delete() actual fun arrayUnion(vararg elements: Any): Any = FieldValue.arrayUnion(*elements) actual fun arrayRemove(vararg elements: Any): Any = FieldValue.arrayRemove(*elements) actual fun delete(): Any = delete +} + +actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { + val android: com.google.firebase.Timestamp = com.google.firebase.Timestamp(seconds, nanoseconds) + + actual val seconds = android.seconds + actual val nanoseconds = android.nanoseconds } \ No newline at end of file diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 366b416b2..7478e8479 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -4,14 +4,33 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.Firebase -import dev.gitlive.firebase.FirebaseApp -import dev.gitlive.firebase.FirebaseException +import dev.gitlive.firebase.* import kotlinx.coroutines.flow.Flow import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.serializer import kotlin.js.JsName +expect fun firestoreSerializer(value: Any): SerializationStrategy<*> +expect fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> +expect class FirestoreEncoder(shouldEncodeElementDefault: Boolean) : FirebaseEncoder +expect class FirestoreDecoder(value: Any?) : FirebaseDecoder + +@Suppress("UNCHECKED_CAST") +inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = value?.let { + FirestoreEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(firestoreSerializer(it) as SerializationStrategy, it) }.value +} + +@Suppress("UNCHECKED_CAST") +inline fun decode(value: Any?): T { + return decode(firestoreDeserializer(value) as DeserializationStrategy, value) +} + +fun decode(strategy: DeserializationStrategy, value: Any?): T { + require(value != null || strategy.descriptor.isNullable) { "Value was null for non-nullable type ${strategy.descriptor.serialName}" } + return FirestoreDecoder(value).decodeSerializableValue(strategy) +} + /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ expect val Firebase.firestore: FirebaseFirestore @@ -218,7 +237,7 @@ expect class FieldPath(vararg fieldNames: String) { } expect object FieldValue { - val serverTimestamp: Double + val serverTimestamp: Any val delete: Any fun arrayUnion(vararg elements: Any): Any fun arrayRemove(vararg elements: Any): Any @@ -226,3 +245,8 @@ expect object FieldValue { @JsName("deprecatedDelete") fun delete(): Any } + +expect class Timestamp(seconds: Long, nanoseconds: Int) { + val seconds: Long + val nanoseconds: Int +} \ No newline at end of file diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 02dfb91c5..7c95472ac 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -14,22 +14,47 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.runBlocking import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.serializer import platform.Foundation.NSError import platform.Foundation.NSNull -@PublishedApi -internal inline fun decode(value: Any?): T = - decode(value) { (it as? FIRTimestamp)?.run { seconds * 1000 + (nanoseconds / 1000000.0) } } +actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FIRFieldValue, is Timestamp -> DummySerializer + else -> value::class.serializer() + } + +actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = + when (value) { + is FIRTimestamp -> DummySerializer + null -> Unit::class.serializer() + else -> value::class.serializer() + } -internal fun decode(strategy: DeserializationStrategy, value: Any?): T = - decode(strategy, value) { (it as? FIRTimestamp)?.run { seconds * 1000 + (nanoseconds / 1000000.0) } } +actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { + override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) + + override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = + when (value) { + is Timestamp -> this.value = value.ios + is FIRFieldValue -> this.value = value + else -> super.encodeSerializableValue(serializer, value) + } +} -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - encode(value, shouldEncodeElementDefault, FIRFieldValue.fieldValueForServerTimestamp()) +@Suppress("UNCHECKED_CAST") +actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDecoder(value) { + override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) -private fun encode(strategy: SerializationStrategy , value: T, shouldEncodeElementDefault: Boolean): Any? = - encode(strategy, value, shouldEncodeElementDefault, FIRFieldValue.fieldValueForServerTimestamp()) + override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = + when (value) { + is FIRTimestamp -> Timestamp(value.seconds, value.nanoseconds) as T + else -> super.decodeSerializableValue(deserializer) + } +} actual val Firebase.firestore get() = FirebaseFirestore(FIRFirestore.firestore()) @@ -422,7 +447,7 @@ actual class FieldPath private constructor(val ios: FIRFieldPath) { } actual object FieldValue { - actual val serverTimestamp = Double.POSITIVE_INFINITY + actual val serverTimestamp: Any get() = FIRFieldValue.fieldValueForServerTimestamp() actual val delete: Any get() = FIRFieldValue.fieldValueForDelete() actual fun arrayUnion(vararg elements: Any): Any = FIRFieldValue.fieldValueForArrayUnion(elements.asList()) actual fun arrayRemove(vararg elements: Any): Any = FIRFieldValue.fieldValueForArrayRemove(elements.asList()) @@ -465,3 +490,9 @@ suspend inline fun await(function: (callback: (NSError?) -> Unit) -> T): T { job.await() return result } + +actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { + val ios: FIRTimestamp = FIRTimestamp(seconds, nanoseconds) + actual val seconds : Long get() = ios.seconds + actual val nanoseconds : Int get() = ios.nanoseconds +} \ No newline at end of file diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index fadb1a6b2..3f1ac40a2 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -12,21 +12,47 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.promise import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.serializer import kotlin.js.json -@PublishedApi -internal inline fun decode(value: Any?): T = - decode(value) { it.takeIf { it.asDynamic().toMillis != undefined }?.asDynamic().toMillis() as? Double } +actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is firebase.firestore.FieldValue, is Timestamp -> DummySerializer + else -> value::class.serializer() + } + +actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = + when (value) { + is firebase.firestore.Timestamp -> DummySerializer + null -> Unit::class.serializer() + else -> value::class.serializer() + } -internal fun decode(strategy: DeserializationStrategy, value: Any?): T = - decode(strategy, value) { it.takeIf { it.asDynamic().toMillis != undefined }?.asDynamic().toMillis() as? Double } -@PublishedApi -internal inline fun encode(value: T, shouldEncodeElementDefault: Boolean) = - encode(value, shouldEncodeElementDefault, firebase.firestore.FieldValue.serverTimestamp()) +actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { + override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) + + override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = + when (value) { + is Timestamp -> this.value = value.js + is firebase.firestore.FieldValue -> this.value = value + else -> super.encodeSerializableValue(serializer, value) + } +} -private fun encode(strategy: SerializationStrategy , value: T, shouldEncodeElementDefault: Boolean): Any? = - encode(strategy, value, shouldEncodeElementDefault, firebase.firestore.FieldValue.serverTimestamp()) +@Suppress("UNCHECKED_CAST") +actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDecoder(value) { + override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) + + override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = + when (value) { + is firebase.firestore.Timestamp -> Timestamp(value.seconds.toLong(), value.nanoseconds.toInt()) as T + else -> super.decodeSerializableValue(deserializer) + } +} actual val Firebase.firestore get() = rethrow { dev.gitlive.firebase.firestore; FirebaseFirestore(firebase.firestore()) } @@ -420,7 +446,7 @@ actual class FieldPath private constructor(val js: firebase.firestore.FieldPath) } actual object FieldValue { - actual val serverTimestamp = Double.POSITIVE_INFINITY + actual val serverTimestamp: Any get() = rethrow { firebase.firestore.FieldValue.serverTimestamp() } actual val delete: Any get() = rethrow { firebase.firestore.FieldValue.delete() } actual fun arrayUnion(vararg elements: Any): Any = rethrow { firebase.firestore.FieldValue.arrayUnion(*elements) } actual fun arrayRemove(vararg elements: Any): Any = rethrow { firebase.firestore.FieldValue.arrayRemove(*elements) } @@ -428,6 +454,12 @@ actual object FieldValue { actual fun delete(): Any = delete } +actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { + val js: firebase.firestore.Timestamp = firebase.firestore.Timestamp() //TODO: Initialize this correctly + actual val seconds : Long get() = js.seconds.toLong() + actual val nanoseconds : Int get() = js.nanoseconds.toInt() +} + //actual data class FirebaseFirestoreSettings internal constructor( // val cacheSizeBytes: Number? = undefined, // val host: String? = undefined, From d1bf9587e8191a0f6723fe5f8db8002b6e891586 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sat, 20 Nov 2021 09:42:37 -0700 Subject: [PATCH 04/13] Reverted group name back to dev.gitlive --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 20e08f8ee..e512e1f2a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,7 @@ tasks { subprojects { - group = "dev.litclimbing" + group = "dev.gitlive" apply(plugin="com.adarshr.test-logger") From 373c115ad73d315747ba5d4ddaff74a9ee77cd06 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sat, 20 Nov 2021 13:17:48 -0700 Subject: [PATCH 05/13] Added support for decoding maps and lists on Android. Removed pointless generic from data function as it always returns a map. --- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 8 +++++--- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 4 +--- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 2 +- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index f91a0f65d..ec5e7f762 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -28,7 +28,7 @@ actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = when (value) { - is com.google.firebase.Timestamp -> DummySerializer + is com.google.firebase.Timestamp, is ArrayList<*>, is HashMap<*, *> -> DummySerializer null -> Unit::class.serializer() else -> value::class.serializer() } @@ -46,11 +46,13 @@ actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boo @Suppress("UNCHECKED_CAST") actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDecoder(value) { - override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) + override fun getDecoder(value: Any?): FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = when (value) { is com.google.firebase.Timestamp -> Timestamp(value.seconds, value.nanoseconds) as T + is ArrayList<*> -> value.map { decode(it) } as T + is HashMap<*, *> -> value.mapValues { entry -> decode(entry.value) } as T else -> super.decodeSerializableValue(deserializer) } } @@ -463,7 +465,7 @@ actual class DocumentSnapshot(val android: com.google.firebase.firestore.Documen actual val id get() = android.id actual val reference get() = DocumentReference(android.reference) - actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = + actual fun data(serverTimestampBehavior: ServerTimestampBehavior): Map = decode(value = android.getData(serverTimestampBehavior.toAndroid())) actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 7478e8479..2792413e7 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -8,7 +8,6 @@ import dev.gitlive.firebase.* import kotlinx.coroutines.flow.Flow import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.serializer import kotlin.js.JsName expect fun firestoreSerializer(value: Any): SerializationStrategy<*> @@ -206,13 +205,12 @@ expect class DocumentChange { } expect class DocumentSnapshot { - inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T fun contains(field: String): Boolean - inline fun data(serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T + fun data(serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): Map fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T val exists: Boolean diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 7c95472ac..0d67033f2 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -403,7 +403,7 @@ actual class DocumentSnapshot(val ios: FIRDocumentSnapshot) { actual val reference get() = DocumentReference(ios.reference) - actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T { + actual fun data(serverTimestampBehavior: ServerTimestampBehavior): Map { val data = ios.dataWithServerTimestampBehavior(serverTimestampBehavior.toIos()) return decode(value = data?.mapValues { (_, value) -> value?.takeIf { it !is NSNull } }) } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 3f1ac40a2..45b6f70f5 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -413,7 +413,7 @@ actual class DocumentSnapshot(val js: firebase.firestore.DocumentSnapshot) { actual val id get() = rethrow { js.id } actual val reference get() = rethrow { DocumentReference(js.ref) } - actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = + actual fun data(serverTimestampBehavior: ServerTimestampBehavior): Map = rethrow { decode(value = js.data(getTimestampsOptions(serverTimestampBehavior))) } actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = From 440b90a85fe280103036f9c9df51f9b3b3b99d52 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sat, 20 Nov 2021 13:59:13 -0700 Subject: [PATCH 06/13] Added support for decoding maps and lists on iOS. --- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 0d67033f2..2fd8b99cd 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -29,7 +29,7 @@ actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = when (value) { - is FIRTimestamp -> DummySerializer + is FIRTimestamp, is Map<*,*>, is List<*> -> DummySerializer null -> Unit::class.serializer() else -> value::class.serializer() } @@ -52,6 +52,8 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = when (value) { is FIRTimestamp -> Timestamp(value.seconds, value.nanoseconds) as T + is Map<*,*> -> value.mapKeys{it.key.toString()}.mapValues { decode(it.value) } as T + is List<*> -> value.map { decode(it) } as T else -> super.decodeSerializableValue(deserializer) } } From 389ca5510f7d052b0589bd9f0730c5c9a17107e5 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sun, 21 Nov 2021 10:20:42 -0700 Subject: [PATCH 07/13] Added GeoPoint to Android and iOS and prioritized class serializers over "builtin" versions --- .../gitlive/firebase/firestore/firestore.kt | 62 ++++++++++++------- .../gitlive/firebase/firestore/firestore.kt | 5 ++ .../gitlive/firebase/firestore/firestore.kt | 60 ++++++++++++------ .../gitlive/firebase/firestore/firestore.kt | 53 +++++++++------- 4 files changed, 117 insertions(+), 63 deletions(-) diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index ec5e7f762..1aa8ac273 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -18,42 +18,56 @@ import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.serializer actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is FieldValue, is Timestamp -> DummySerializer - else -> value::class.serializer() + runCatching {value::class.serializer()}.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FieldValue, is Timestamp, is GeoPoint -> DummySerializer + else -> throw it + } } actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - when (value) { - is com.google.firebase.Timestamp, is ArrayList<*>, is HashMap<*, *> -> DummySerializer - null -> Unit::class.serializer() - else -> value::class.serializer() + runCatching {value!!::class.serializer()}.getOrElse { + when (value) { + is com.google.firebase.Timestamp, is com.google.firebase.firestore.GeoPoint, is ArrayList<*>, is HashMap<*, *> -> DummySerializer + null -> Unit::class.serializer() + else -> throw it + } } actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - when (value) { - is Timestamp -> this.value = value.android - is FieldValue -> this.value = value - else -> super.encodeSerializableValue(serializer, value) + if (serializer is DummySerializer) { + when (value) { + is Timestamp -> this.value = value.android + is GeoPoint -> this.value = value.android + is FieldValue -> this.value = value + else -> error("Unsupported encode type ${value?.let{it::class.qualifiedName}} with DummySerializer!") + } + } else { + super.encodeSerializableValue(serializer, value) } } @Suppress("UNCHECKED_CAST") actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDecoder(value) { - override fun getDecoder(value: Any?): FirebaseDecoder = FirestoreDecoder(value) + override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - when (value) { - is com.google.firebase.Timestamp -> Timestamp(value.seconds, value.nanoseconds) as T - is ArrayList<*> -> value.map { decode(it) } as T - is HashMap<*, *> -> value.mapValues { entry -> decode(entry.value) } as T - else -> super.decodeSerializableValue(deserializer) + if (deserializer is DummySerializer) { + when (value) { + is com.google.firebase.Timestamp -> Timestamp(value.seconds, value.nanoseconds) as T + is com.google.firebase.firestore.GeoPoint -> GeoPoint(value.latitude, value.longitude) as T + is ArrayList<*> -> value.map { decode(it) } as T + is HashMap<*, *> -> value.mapValues { entry -> decode(entry.value) } as T + else -> error("Unsupported decode type ${value?.let{it::class.qualifiedName}} with DummySerializer!") + } + } else { + super.decodeSerializableValue(deserializer) } } @@ -510,8 +524,14 @@ actual object FieldValue { } actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { - val android: com.google.firebase.Timestamp = com.google.firebase.Timestamp(seconds, nanoseconds) + val android = com.google.firebase.Timestamp(seconds, nanoseconds) actual val seconds = android.seconds actual val nanoseconds = android.nanoseconds +} + +actual class GeoPoint actual constructor(latitude: Double, longitude: Double) { + val android = com.google.firebase.firestore.GeoPoint(latitude, longitude) + actual val latitude = android.latitude + actual val longitude = android.longitude } \ No newline at end of file diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 2792413e7..e99a86a1a 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -247,4 +247,9 @@ expect object FieldValue { expect class Timestamp(seconds: Long, nanoseconds: Int) { val seconds: Long val nanoseconds: Int +} + +expect class GeoPoint(latitude: Double, longitude: Double) { + val latitude: Double + val longitude: Double } \ No newline at end of file diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 2fd8b99cd..a1e559f97 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -19,29 +19,38 @@ import platform.Foundation.NSError import platform.Foundation.NSNull actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is FIRFieldValue, is Timestamp -> DummySerializer - else -> value::class.serializer() + runCatching {value::class.serializer()}.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FIRFieldValue, is GeoPoint, is Timestamp -> DummySerializer + else -> throw it + } } actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - when (value) { - is FIRTimestamp, is Map<*,*>, is List<*> -> DummySerializer - null -> Unit::class.serializer() - else -> value::class.serializer() + runCatching {value!!::class.serializer()}.getOrElse { + when (value) { + is FIRTimestamp, is FIRGeoPoint, is Map<*,*>, is List<*> -> DummySerializer + null -> Unit::class.serializer() + else -> throw it + } } actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - when (value) { - is Timestamp -> this.value = value.ios - is FIRFieldValue -> this.value = value - else -> super.encodeSerializableValue(serializer, value) + if (serializer is DummySerializer) { + when (value) { + is Timestamp -> this.value = value.ios + is GeoPoint -> this.value = value.ios + is FIRFieldValue -> this.value = value + else -> error("Unsupported encode type ${value?.let{it::class.qualifiedName}} with DummySerializer!") + } + } else { + super.encodeSerializableValue(serializer, value) } } @@ -50,11 +59,16 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - when (value) { - is FIRTimestamp -> Timestamp(value.seconds, value.nanoseconds) as T - is Map<*,*> -> value.mapKeys{it.key.toString()}.mapValues { decode(it.value) } as T - is List<*> -> value.map { decode(it) } as T - else -> super.decodeSerializableValue(deserializer) + if (deserializer is DummySerializer) { + when (value) { + is FIRTimestamp -> Timestamp(value.seconds, value.nanoseconds) as T + is FIRGeoPoint -> GeoPoint(value.latitude, value.longitude) as T + is Map<*,*> -> value.mapValues { decode(it.value) } as T + is List<*> -> value.map { decode(it) } as T + else -> error("Unsupported decode type ${value?.let{it::class.qualifiedName}} with DummySerializer!") + } + } else { + super.decodeSerializableValue(deserializer) } } @@ -494,7 +508,13 @@ suspend inline fun await(function: (callback: (NSError?) -> Unit) -> T): T { } actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { - val ios: FIRTimestamp = FIRTimestamp(seconds, nanoseconds) + val ios = FIRTimestamp(seconds, nanoseconds) actual val seconds : Long get() = ios.seconds actual val nanoseconds : Int get() = ios.nanoseconds +} + +actual class GeoPoint actual constructor(latitude: Double, longitude: Double) { + val ios = FIRGeoPoint(latitude, longitude) + actual val latitude = ios.latitude + actual val longitude = ios.longitude } \ No newline at end of file diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 45b6f70f5..3a1a05b2f 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -16,19 +16,23 @@ import kotlinx.serialization.serializer import kotlin.js.json actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is firebase.firestore.FieldValue, is Timestamp -> DummySerializer - else -> value::class.serializer() + runCatching {value::class.serializer()}.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is firebase.firestore.FieldValue, is Timestamp -> DummySerializer // TODO: Add GeoPoint + else -> throw it + } } actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - when (value) { - is firebase.firestore.Timestamp -> DummySerializer - null -> Unit::class.serializer() - else -> value::class.serializer() + runCatching {value!!::class.serializer()}.getOrElse { + when (value) { + is firebase.firestore.Timestamp, is List<*>, is Map<*, *> -> DummySerializer // TODO: Add GeoPoint + null -> Unit::class.serializer() + else -> throw it + } } @@ -36,10 +40,14 @@ actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boo override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - when (value) { - is Timestamp -> this.value = value.js - is firebase.firestore.FieldValue -> this.value = value - else -> super.encodeSerializableValue(serializer, value) + if (serializer is DummySerializer) { + when (value) { + is Timestamp -> TODO("Not sure how to properly intantiate for JS") + is firebase.firestore.FieldValue -> this.value = value + else -> error("Unsupported encode type with DummySerializer!") + } + } else { + super.encodeSerializableValue(serializer, value) } } @@ -48,9 +56,13 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - when (value) { - is firebase.firestore.Timestamp -> Timestamp(value.seconds.toLong(), value.nanoseconds.toInt()) as T - else -> super.decodeSerializableValue(deserializer) + if (deserializer is DummySerializer) { + when (value) { + is firebase.firestore.Timestamp -> Timestamp(value.seconds.toLong(), value.nanoseconds.toInt()) as T + else -> error("Unsupported decode type with DummySerializer!") + } + } else { + super.decodeSerializableValue(deserializer) } } @@ -454,11 +466,8 @@ actual object FieldValue { actual fun delete(): Any = delete } -actual class Timestamp actual constructor(seconds: Long, nanoseconds: Int) { - val js: firebase.firestore.Timestamp = firebase.firestore.Timestamp() //TODO: Initialize this correctly - actual val seconds : Long get() = js.seconds.toLong() - actual val nanoseconds : Int get() = js.nanoseconds.toInt() -} +actual class Timestamp actual constructor(actual val seconds: Long, actual val nanoseconds: Int) +actual class GeoPoint actual constructor(actual val latitude: Double, actual val longitude: Double) //actual data class FirebaseFirestoreSettings internal constructor( // val cacheSizeBytes: Number? = undefined, From 09e1e3f6bccfabd0b69a5d20b1ef17c8cc081719 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sun, 21 Nov 2021 11:15:17 -0700 Subject: [PATCH 08/13] Reworked the serializer resolver to prefer the generic type and value type over built in types. --- .../gitlive/firebase/firestore/firestore.kt | 35 +++--- .../gitlive/firebase/firestore/firestore.kt | 100 +++++++++++++++--- .../gitlive/firebase/firestore/firestore.kt | 35 +++--- .../gitlive/firebase/firestore/firestore.kt | 36 +++---- 4 files changed, 135 insertions(+), 71 deletions(-) diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 1aa8ac273..1f6156bea 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -14,28 +14,27 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.serializer -actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - runCatching {value::class.serializer()}.getOrElse { - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is FieldValue, is Timestamp, is GeoPoint -> DummySerializer - else -> throw it - } - } +@Suppress("UNCHECKED_CAST") +actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::getSerializer) + is List<*> -> FirebaseListSerializer(::getSerializer) + is Set<*> -> FirebaseListSerializer(::getSerializer) + is FieldValue, is Timestamp, is GeoPoint -> DummySerializer + else -> onFailure() + } as KSerializer -actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - runCatching {value!!::class.serializer()}.getOrElse { - when (value) { - is com.google.firebase.Timestamp, is com.google.firebase.firestore.GeoPoint, is ArrayList<*>, is HashMap<*, *> -> DummySerializer - null -> Unit::class.serializer() - else -> throw it - } - } +@Suppress("UNCHECKED_CAST") +actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = + when (value) { + is com.google.firebase.Timestamp, is com.google.firebase.firestore.GeoPoint, is ArrayList<*>, is HashMap<*, *> -> DummySerializer + null -> Unit::class.serializer() + else -> onFailure() + } as KSerializer actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index e99a86a1a..82a2e76a6 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -7,22 +7,40 @@ package dev.gitlive.firebase.firestore import dev.gitlive.firebase.* import kotlinx.coroutines.flow.Flow import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.serializer import kotlin.js.JsName -expect fun firestoreSerializer(value: Any): SerializationStrategy<*> -expect fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> +expect fun firestoreSerializer(value: T, onFailure: () -> Nothing): KSerializer +expect inline fun firestoreDeserializer(value: Any?, onFailure: () -> Nothing): KSerializer expect class FirestoreEncoder(shouldEncodeElementDefault: Boolean) : FirebaseEncoder expect class FirestoreDecoder(value: Any?) : FirebaseDecoder @Suppress("UNCHECKED_CAST") +fun getSerializer(value: T): KSerializer = + runCatching { value::class.serializer() }.getOrElse { + firestoreSerializer(value) { throw it } + } as KSerializer + +@Suppress("UNCHECKED_CAST") +inline fun getDeserializer(value: Any?): KSerializer = + runCatching { serializer() }.getOrElse { first -> + if (value is T) { // value may be extension of T and have it's own serializer + runCatching { value!!::class.serializer() }.getOrElse { second -> + firestoreDeserializer(value) { throw first } + } + } else { + firestoreDeserializer(value) { throw first } + } + } as KSerializer + inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = value?.let { - FirestoreEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(firestoreSerializer(it) as SerializationStrategy, it) }.value + FirestoreEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(getSerializer(it), it) }.value } -@Suppress("UNCHECKED_CAST") inline fun decode(value: Any?): T { - return decode(firestoreDeserializer(value) as DeserializationStrategy, value) + return decode(getDeserializer(value), value) } fun decode(strategy: DeserializationStrategy, value: Any?): T { @@ -56,9 +74,29 @@ expect class Transaction { fun set(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean = true, vararg mergeFields: String): Transaction fun set(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean = true, vararg mergeFieldPaths: FieldPath): Transaction - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): Transaction - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, vararg mergeFields: String): Transaction - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, vararg mergeFieldPaths: FieldPath): Transaction + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + merge: Boolean = false + ): Transaction + + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + vararg mergeFields: String + ): Transaction + + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + vararg mergeFieldPaths: FieldPath + ): Transaction fun update(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean = true): Transaction fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true): Transaction @@ -91,8 +129,12 @@ fun Query.where(field: String, equalTo: Any?) = _where(field, equalTo) fun Query.where(path: FieldPath, equalTo: Any?) = _where(path, equalTo) fun Query.where(field: String, equalTo: DocumentReference) = _where(field, equalTo) fun Query.where(path: FieldPath, equalTo: DocumentReference) = _where(path, equalTo) -fun Query.where(field: String, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = _where(field, lessThan, greaterThan, arrayContains) -fun Query.where(path: FieldPath, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = _where(path, lessThan, greaterThan, arrayContains) +fun Query.where(field: String, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = + _where(field, lessThan, greaterThan, arrayContains) + +fun Query.where(path: FieldPath, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = + _where(path, lessThan, greaterThan, arrayContains) + fun Query.where(field: String, inArray: List? = null, arrayContainsAny: List? = null) = _where(field, inArray, arrayContainsAny) fun Query.where(path: FieldPath, inArray: List? = null, arrayContainsAny: List? = null) = _where(path, inArray, arrayContainsAny) @@ -104,9 +146,29 @@ expect class WriteBatch { inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, vararg mergeFields: String): WriteBatch inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, vararg mergeFieldPaths: FieldPath): WriteBatch - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): WriteBatch - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, vararg mergeFields: String): WriteBatch - fun set(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true, vararg mergeFieldPaths: FieldPath): WriteBatch + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + merge: Boolean = false + ): WriteBatch + + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + vararg mergeFields: String + ): WriteBatch + + fun set( + documentRef: DocumentReference, + strategy: SerializationStrategy, + data: T, + encodeDefaults: Boolean = true, + vararg mergeFieldPaths: FieldPath + ): WriteBatch inline fun update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true): WriteBatch fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true): WriteBatch @@ -150,6 +212,7 @@ expect class CollectionReference : Query { fun document(documentPath: String): DocumentReference suspend inline fun add(data: T, encodeDefaults: Boolean = true): DocumentReference + @Deprecated("This will be replaced with add(strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true)") suspend fun add(data: T, strategy: SerializationStrategy, encodeDefaults: Boolean = true): DocumentReference suspend fun add(strategy: SerializationStrategy, data: T, encodeDefaults: Boolean = true): DocumentReference @@ -192,7 +255,7 @@ expect class QuerySnapshot { } expect enum class ChangeType { - ADDED , + ADDED, MODIFIED, REMOVED } @@ -206,12 +269,16 @@ expect class DocumentChange { expect class DocumentSnapshot { inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T - fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T + fun get( + field: String, + strategy: DeserializationStrategy, + serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE + ): T fun contains(field: String): Boolean fun data(serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): Map - fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T + fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T val exists: Boolean val id: String @@ -239,6 +306,7 @@ expect object FieldValue { val delete: Any fun arrayUnion(vararg elements: Any): Any fun arrayRemove(vararg elements: Any): Any + @Deprecated("Replaced with FieldValue.delete") @JsName("deprecatedDelete") fun delete(): Any diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index a1e559f97..710ae53d2 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -13,30 +13,29 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.runBlocking import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.serializer import platform.Foundation.NSError import platform.Foundation.NSNull -actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - runCatching {value::class.serializer()}.getOrElse { - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is FIRFieldValue, is GeoPoint, is Timestamp -> DummySerializer - else -> throw it - } - } +@Suppress("UNCHECKED_CAST") +actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::getSerializer) + is List<*> -> FirebaseListSerializer(::getSerializer) + is Set<*> -> FirebaseListSerializer(::getSerializer) + is FIRFieldValue, is GeoPoint, is Timestamp -> DummySerializer + else -> onFailure() + } as KSerializer -actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - runCatching {value!!::class.serializer()}.getOrElse { - when (value) { - is FIRTimestamp, is FIRGeoPoint, is Map<*,*>, is List<*> -> DummySerializer - null -> Unit::class.serializer() - else -> throw it - } - } +@Suppress("UNCHECKED_CAST") +actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = + when (value) { + is FIRTimestamp, is FIRGeoPoint, is Map<*, *>, is List<*> -> DummySerializer + null -> Unit::class.serializer() + else -> onFailure() + } as KSerializer actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 3a1a05b2f..a58f83c0f 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -11,30 +11,28 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.promise import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.serializer import kotlin.js.json -actual fun firestoreSerializer(value: Any): SerializationStrategy<*> = - runCatching {value::class.serializer()}.getOrElse { - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) - is List<*> -> FirebaseListSerializer(::firestoreSerializer) - is Set<*> -> FirebaseListSerializer(::firestoreSerializer) - is firebase.firestore.FieldValue, is Timestamp -> DummySerializer // TODO: Add GeoPoint - else -> throw it - } - } - -actual fun firestoreDeserializer(value: Any?): DeserializationStrategy<*> = - runCatching {value!!::class.serializer()}.getOrElse { - when (value) { - is firebase.firestore.Timestamp, is List<*>, is Map<*, *> -> DummySerializer // TODO: Add GeoPoint - null -> Unit::class.serializer() - else -> throw it - } - } +@Suppress("UNCHECKED_CAST") +actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::getSerializer) + is List<*> -> FirebaseListSerializer(::getSerializer) + is Set<*> -> FirebaseListSerializer(::getSerializer) + is firebase.firestore.FieldValue, is Timestamp -> DummySerializer // TODO: Add GeoPoint + else -> onFailure() + } as KSerializer +@Suppress("UNCHECKED_CAST") +actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = + when (value) { + is firebase.firestore.Timestamp, is List<*>, is Map<*, *> -> DummySerializer // TODO: Add GeoPoint + null -> Unit::class.serializer() + else -> onFailure() + } as KSerializer actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) From 7763fd838005350dd2c4968e9940d1af8f88bb57 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Sun, 21 Nov 2021 13:13:45 -0700 Subject: [PATCH 09/13] Reworked the serializer resolver to prefer the generic type and value type over built in types. --- .../gitlive/firebase/firestore/firestore.kt | 44 ++++++++++--------- .../gitlive/firebase/firestore/firestore.kt | 40 +++++------------ .../gitlive/firebase/firestore/firestore.kt | 44 ++++++++++--------- .../gitlive/firebase/firestore/firestore.kt | 44 ++++++++++--------- 4 files changed, 83 insertions(+), 89 deletions(-) diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 1f6156bea..4837f0287 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -13,34 +13,38 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.serializer +import kotlinx.serialization.* @Suppress("UNCHECKED_CAST") -actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::getSerializer) - is List<*> -> FirebaseListSerializer(::getSerializer) - is Set<*> -> FirebaseListSerializer(::getSerializer) - is FieldValue, is Timestamp, is GeoPoint -> DummySerializer - else -> onFailure() - } as KSerializer +actual fun firestoreSerializer(value: T): SerializationStrategy = + runCatching { value::class.serializer() }.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FieldValue, is Timestamp, is GeoPoint -> DummySerializer + else -> throw it + } + }as SerializationStrategy @Suppress("UNCHECKED_CAST") -actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = - when (value) { - is com.google.firebase.Timestamp, is com.google.firebase.firestore.GeoPoint, is ArrayList<*>, is HashMap<*, *> -> DummySerializer - null -> Unit::class.serializer() - else -> onFailure() - } as KSerializer +actual inline fun firestoreDeserializer(value: Any?): DeserializationStrategy = + runCatching { serializer() }.getOrElse { + if (value != null && value is T) { + value::class.serializerOrNull()?.also { s -> return@getOrElse s } + } + when (value) { + is com.google.firebase.Timestamp, is com.google.firebase.firestore.GeoPoint, is ArrayList<*>, is HashMap<*, *> -> DummySerializer + null -> Unit::class.serializer() + else -> throw it + } + } as DeserializationStrategy actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - if (serializer is DummySerializer) { + if (serializer === DummySerializer) { when (value) { is Timestamp -> this.value = value.android is GeoPoint -> this.value = value.android @@ -57,7 +61,7 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - if (deserializer is DummySerializer) { + if (deserializer === DummySerializer) { when (value) { is com.google.firebase.Timestamp -> Timestamp(value.seconds, value.nanoseconds) as T is com.google.firebase.firestore.GeoPoint -> GeoPoint(value.latitude, value.longitude) as T diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 82a2e76a6..4c2bb7f2e 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -12,35 +12,17 @@ import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.serializer import kotlin.js.JsName -expect fun firestoreSerializer(value: T, onFailure: () -> Nothing): KSerializer -expect inline fun firestoreDeserializer(value: Any?, onFailure: () -> Nothing): KSerializer +expect fun firestoreSerializer(value: T): SerializationStrategy +expect inline fun firestoreDeserializer(value: Any?): DeserializationStrategy expect class FirestoreEncoder(shouldEncodeElementDefault: Boolean) : FirebaseEncoder expect class FirestoreDecoder(value: Any?) : FirebaseDecoder -@Suppress("UNCHECKED_CAST") -fun getSerializer(value: T): KSerializer = - runCatching { value::class.serializer() }.getOrElse { - firestoreSerializer(value) { throw it } - } as KSerializer - -@Suppress("UNCHECKED_CAST") -inline fun getDeserializer(value: Any?): KSerializer = - runCatching { serializer() }.getOrElse { first -> - if (value is T) { // value may be extension of T and have it's own serializer - runCatching { value!!::class.serializer() }.getOrElse { second -> - firestoreDeserializer(value) { throw first } - } - } else { - firestoreDeserializer(value) { throw first } - } - } as KSerializer - -inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = value?.let { - FirestoreEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(getSerializer(it), it) }.value +inline fun encode(value: T, shouldEncodeElementDefault: Boolean = true): Any? = value?.let { + FirestoreEncoder(shouldEncodeElementDefault).apply { encodeSerializableValue(firestoreSerializer(it), it) }.value } inline fun decode(value: Any?): T { - return decode(getDeserializer(value), value) + return decode(firestoreDeserializer(value), value) } fun decode(strategy: DeserializationStrategy, value: Any?): T { @@ -125,18 +107,18 @@ expect open class Query { internal fun _orderBy(field: FieldPath, direction: Direction): Query } -fun Query.where(field: String, equalTo: Any?) = _where(field, equalTo) -fun Query.where(path: FieldPath, equalTo: Any?) = _where(path, equalTo) +fun Query.where(field: String, equalTo: Any?) = _where(field, encode(equalTo)) +fun Query.where(path: FieldPath, equalTo: Any?) = _where(path, encode(equalTo)) fun Query.where(field: String, equalTo: DocumentReference) = _where(field, equalTo) fun Query.where(path: FieldPath, equalTo: DocumentReference) = _where(path, equalTo) fun Query.where(field: String, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = - _where(field, lessThan, greaterThan, arrayContains) + _where(field, encode(lessThan), encode(greaterThan), encode(arrayContains)) fun Query.where(path: FieldPath, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = - _where(path, lessThan, greaterThan, arrayContains) + _where(path, encode(lessThan), encode(greaterThan), encode(arrayContains)) -fun Query.where(field: String, inArray: List? = null, arrayContainsAny: List? = null) = _where(field, inArray, arrayContainsAny) -fun Query.where(path: FieldPath, inArray: List? = null, arrayContainsAny: List? = null) = _where(path, inArray, arrayContainsAny) +fun Query.where(field: String, inArray: List? = null, arrayContainsAny: List? = null) = _where(field, encode(inArray), encode(arrayContainsAny)) +fun Query.where(path: FieldPath, inArray: List? = null, arrayContainsAny: List? = null) = _where(path, encode(inArray), encode(arrayContainsAny)) fun Query.orderBy(field: String, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction) fun Query.orderBy(field: FieldPath, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction) diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 710ae53d2..e98eb8948 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -12,36 +12,40 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.runBlocking -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.serializer +import kotlinx.serialization.* import platform.Foundation.NSError import platform.Foundation.NSNull @Suppress("UNCHECKED_CAST") -actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::getSerializer) - is List<*> -> FirebaseListSerializer(::getSerializer) - is Set<*> -> FirebaseListSerializer(::getSerializer) - is FIRFieldValue, is GeoPoint, is Timestamp -> DummySerializer - else -> onFailure() - } as KSerializer +actual fun firestoreSerializer(value: T): SerializationStrategy = + runCatching { value::class.serializer() }.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is FIRFieldValue, is GeoPoint, is Timestamp -> DummySerializer + else -> throw it + } + }as SerializationStrategy @Suppress("UNCHECKED_CAST") -actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = - when (value) { - is FIRTimestamp, is FIRGeoPoint, is Map<*, *>, is List<*> -> DummySerializer - null -> Unit::class.serializer() - else -> onFailure() - } as KSerializer +actual inline fun firestoreDeserializer(value: Any?): DeserializationStrategy = + runCatching { serializer() }.getOrElse { + if (value != null && value is T) { + value::class.serializerOrNull()?.also { s -> return@getOrElse s } + } + when (value) { + is FIRTimestamp, is FIRGeoPoint, is Map<*, *>, is List<*> -> DummySerializer + null -> Unit::class.serializer() + else -> throw it + } + } as DeserializationStrategy actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - if (serializer is DummySerializer) { + if (serializer === DummySerializer) { when (value) { is Timestamp -> this.value = value.ios is GeoPoint -> this.value = value.ios @@ -58,7 +62,7 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - if (deserializer is DummySerializer) { + if (deserializer === DummySerializer) { when (value) { is FIRTimestamp -> Timestamp(value.seconds, value.nanoseconds) as T is FIRGeoPoint -> GeoPoint(value.latitude, value.longitude) as T diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index a58f83c0f..fb84ea559 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -10,35 +10,39 @@ import kotlinx.coroutines.await import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.promise -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.serializer +import kotlinx.serialization.* import kotlin.js.json @Suppress("UNCHECKED_CAST") -actual fun firestoreSerializer(value: T, onFailure:()->Nothing): KSerializer = - when (value) { - is Map<*, *> -> FirebaseMapSerializer(::getSerializer) - is List<*> -> FirebaseListSerializer(::getSerializer) - is Set<*> -> FirebaseListSerializer(::getSerializer) - is firebase.firestore.FieldValue, is Timestamp -> DummySerializer // TODO: Add GeoPoint - else -> onFailure() - } as KSerializer +actual fun firestoreSerializer(value: T): SerializationStrategy = + runCatching { value::class.serializer() }.getOrElse { + when (value) { + is Map<*, *> -> FirebaseMapSerializer(::firestoreSerializer) + is List<*> -> FirebaseListSerializer(::firestoreSerializer) + is Set<*> -> FirebaseListSerializer(::firestoreSerializer) + is firebase.firestore.FieldValue, is Timestamp -> DummySerializer // TODO: Add GeoPoint + else -> throw it + } + }as SerializationStrategy @Suppress("UNCHECKED_CAST") -actual inline fun firestoreDeserializer(value: Any?, onFailure:()->Nothing): KSerializer = - when (value) { - is firebase.firestore.Timestamp, is List<*>, is Map<*, *> -> DummySerializer // TODO: Add GeoPoint - null -> Unit::class.serializer() - else -> onFailure() - } as KSerializer +actual inline fun firestoreDeserializer(value: Any?): DeserializationStrategy = + runCatching { serializer() }.getOrElse { + if (value != null && value is T) { + value::class.serializerOrNull()?.also { s -> return@getOrElse s } + } + when (value) { + is firebase.firestore.Timestamp, is List<*>, is Map<*, *> -> DummySerializer // TODO: Add GeoPoint + null -> Unit::class.serializer() + else -> throw it + } + } as DeserializationStrategy actual class FirestoreEncoder actual constructor(shouldEncodeElementDefault: Boolean) : FirebaseEncoder(shouldEncodeElementDefault) { override fun getEncoder(shouldEncodeElementDefault: Boolean): FirebaseEncoder = FirestoreEncoder(shouldEncodeElementDefault) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = - if (serializer is DummySerializer) { + if (serializer === DummySerializer) { when (value) { is Timestamp -> TODO("Not sure how to properly intantiate for JS") is firebase.firestore.FieldValue -> this.value = value @@ -54,7 +58,7 @@ actual class FirestoreDecoder actual constructor(val value: Any?) : FirebaseDeco override fun getDecoder(value: Any?) : FirebaseDecoder = FirestoreDecoder(value) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - if (deserializer is DummySerializer) { + if (deserializer === DummySerializer) { when (value) { is firebase.firestore.Timestamp -> Timestamp(value.seconds.toLong(), value.nanoseconds.toInt()) as T else -> error("Unsupported decode type with DummySerializer!") From 29fceb0522ad67880d619bd83f83f429ae15bb6b Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Thu, 9 Dec 2021 09:24:16 -0700 Subject: [PATCH 10/13] Added "parent" field to document and collection references --- build.gradle.kts | 2 +- .../src/jsMain/kotlin/dev/gitlive/firebase/externals.kt | 2 ++ .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 6 ++++++ .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 2 ++ .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 5 +++++ .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 5 +++++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e512e1f2a..fff5e4af1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -197,7 +197,7 @@ subprojects { dependencies { "commonMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") "androidMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2") - "androidMainImplementation"(platform("com.google.firebase:firebase-bom:28.4.1")) + "androidMainImplementation"(platform("com.google.firebase:firebase-bom:29.0.1")) "commonTestImplementation"(kotlin("test-common")) "commonTestImplementation"(kotlin("test-annotations-common")) "commonTestImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt index 3485345b0..9137cfa6c 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt @@ -383,6 +383,7 @@ external object firebase { open class CollectionReference : Query { val path: String + val parent: DocumentReference? fun doc(path: String = definedExternally): DocumentReference fun add(data: Any): Promise } @@ -419,6 +420,7 @@ external object firebase { open class DocumentReference { val id: String val path: String + val parent: CollectionReference fun collection(path: String): CollectionReference fun get(options: Any? = definedExternally): Promise diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 4837f0287..3f5886b8b 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -297,6 +297,9 @@ actual class DocumentReference(val android: com.google.firebase.firestore.Docume actual val path: String get() = android.path + actual val parent: CollectionReference + get() = CollectionReference(android.parent) + actual fun collection(collectionPath: String) = CollectionReference(android.collection(collectionPath)) actual suspend inline fun set(data: T, encodeDefaults: Boolean, merge: Boolean) = when (merge) { @@ -439,6 +442,9 @@ actual class CollectionReference(override val android: com.google.firebase.fires actual val document: DocumentReference get() = DocumentReference(android.document()) + actual val parent: DocumentReference? + get() = android.parent?.let{DocumentReference(it)} + actual fun document(documentPath: String) = DocumentReference(android.document(documentPath)) actual suspend inline fun add(data: T, encodeDefaults: Boolean) = diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 4c2bb7f2e..dde62e515 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -167,6 +167,7 @@ expect class DocumentReference { val id: String val path: String val snapshots: Flow + val parent: CollectionReference fun collection(collectionPath: String): CollectionReference suspend fun get(): DocumentSnapshot @@ -191,6 +192,7 @@ expect class DocumentReference { expect class CollectionReference : Query { val path: String val document: DocumentReference + val parent: DocumentReference? fun document(documentPath: String): DocumentReference suspend inline fun add(data: T, encodeDefaults: Boolean = true): DocumentReference diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index e98eb8948..0f43cd27a 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -218,6 +218,9 @@ actual class DocumentReference(val ios: FIRDocumentReference) { actual val path: String get() = ios.path + actual val parent: CollectionReference + get() = CollectionReference(ios.parent) + actual fun collection(collectionPath: String) = CollectionReference(ios.collectionWithPath(collectionPath)) actual suspend inline fun set(data: T, encodeDefaults: Boolean, merge: Boolean) = @@ -326,6 +329,8 @@ actual class CollectionReference(override val ios: FIRCollectionReference) : Que actual val document get() = DocumentReference(ios.documentWithAutoID()) + actual val parent get() = ios.parent?.let{DocumentReference(it)} + actual fun document(documentPath: String) = DocumentReference(ios.documentWithPath(documentPath)) actual suspend inline fun add(data: T, encodeDefaults: Boolean) = diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index fb84ea559..bc537ea1c 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -254,6 +254,9 @@ actual class DocumentReference(val js: firebase.firestore.DocumentReference) { actual val path: String get() = rethrow { js.path } + actual val parent: CollectionReference + get() = rethrow { CollectionReference(js.parent) } + actual fun collection(collectionPath: String) = rethrow { CollectionReference(js.collection(collectionPath)) } actual suspend inline fun set(data: T, encodeDefaults: Boolean, merge: Boolean) = @@ -387,6 +390,8 @@ actual class CollectionReference(override val js: firebase.firestore.CollectionR actual val document get() = rethrow { DocumentReference(js.doc()) } + actual val parent get() = rethrow { js.parent?.let{DocumentReference(it)} } + actual fun document(documentPath: String) = rethrow { DocumentReference(js.doc(documentPath)) } actual suspend inline fun add(data: T, encodeDefaults: Boolean) = From 1fe9e9ea486b2152e318b0c498c9804f7ab14112 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Mon, 13 Dec 2021 16:48:23 -0700 Subject: [PATCH 11/13] Added startAt/endAt support for Firestore in iOS and Android --- .../gitlive/firebase/firestore/firestore.kt | 10 ++++++++++ .../gitlive/firebase/firestore/firestore.kt | 20 +++++++++++++++++++ .../gitlive/firebase/firestore/firestore.kt | 11 +++++++++- .../gitlive/firebase/firestore/firestore.kt | 10 ++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 3f5886b8b..1cae1e1eb 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -429,6 +429,16 @@ actual open class Query(open val android: com.google.firebase.firestore.Query) { internal actual fun _orderBy(field: String, direction: Direction) = Query(android.orderBy(field, direction)) internal actual fun _orderBy(field: FieldPath, direction: Direction) = Query(android.orderBy(field.android, direction)) + + internal actual fun _startAt(vararg fieldValues: Any?) = Query(android.startAt(*fieldValues)) + internal actual fun _startAt(snapshot: DocumentSnapshot) = Query(android.startAt(snapshot.android)) + internal actual fun _startAfter(vararg fieldValues: Any?) = Query(android.startAfter(*fieldValues)) + internal actual fun _startAfter(snapshot: DocumentSnapshot) = Query(android.startAfter(snapshot.android)) + + internal actual fun _endAt(vararg fieldValues: Any?) = Query(android.endAt(*fieldValues)) + internal actual fun _endAt(snapshot: DocumentSnapshot) = Query(android.endAt(snapshot.android)) + internal actual fun _endBefore(vararg fieldValues: Any?) = Query(android.endBefore(*fieldValues)) + internal actual fun _endBefore(snapshot: DocumentSnapshot) = Query(android.endBefore(snapshot.android)) } actual typealias Direction = com.google.firebase.firestore.Query.Direction diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index dde62e515..f5fc2b7c7 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -105,6 +105,16 @@ expect open class Query { internal fun _orderBy(field: String, direction: Direction): Query internal fun _orderBy(field: FieldPath, direction: Direction): Query + + internal fun _startAt(vararg fieldValues: Any?): Query + internal fun _startAt(snapshot: DocumentSnapshot): Query + internal fun _startAfter(vararg fieldValues: Any?): Query + internal fun _startAfter(snapshot: DocumentSnapshot): Query + + internal fun _endAt(vararg fieldValues: Any?): Query + internal fun _endAt(snapshot: DocumentSnapshot): Query + internal fun _endBefore(vararg fieldValues: Any?): Query + internal fun _endBefore(snapshot: DocumentSnapshot): Query } fun Query.where(field: String, equalTo: Any?) = _where(field, encode(equalTo)) @@ -123,6 +133,16 @@ fun Query.where(path: FieldPath, inArray: List? = null, arrayContainsAny: L fun Query.orderBy(field: String, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction) fun Query.orderBy(field: FieldPath, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction) +fun Query.startAt(vararg fieldValues: Any?) = _startAt(*fieldValues.map { encode(it) }.toTypedArray()) +fun Query.startAt(snapshot: DocumentSnapshot) = _startAt(snapshot) +fun Query.startAfter(vararg fieldValues: Any?) = _startAfter(*fieldValues.map { encode(it) }.toTypedArray()) +fun Query.startAfter(snapshot: DocumentSnapshot) = _startAfter(snapshot) + +fun Query.endAt(vararg fieldValues: Any?) = _endAt(*fieldValues.map { encode(it) }.toTypedArray()) +fun Query.endAt(snapshot: DocumentSnapshot) = _endAt(snapshot) +fun Query.endAfter(vararg fieldValues: Any?) = _endBefore(*fieldValues.map { encode(it) }.toTypedArray()) +fun Query.endAfter(snapshot: DocumentSnapshot) = _endBefore(snapshot) + expect class WriteBatch { inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): WriteBatch inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, vararg mergeFields: String): WriteBatch diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 0f43cd27a..3fcd09ec2 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -317,9 +317,18 @@ actual open class Query(open val ios: FIRQuery) { ) internal actual fun _orderBy(field: String, direction: Direction) = Query(ios.queryOrderedByField(field, direction == Direction.DESCENDING)) - internal actual fun _orderBy(field: FieldPath, direction: Direction) = Query(ios.queryOrderedByFieldPath(field.ios, direction == Direction.DESCENDING)) + internal actual fun _startAt(vararg fieldValues: Any?) = Query(ios.queryStartingAtValues(fieldValues.toList())) + internal actual fun _startAt(snapshot: DocumentSnapshot) = Query(ios.queryStartingAtDocument(snapshot.ios)) + internal actual fun _startAfter(vararg fieldValues: Any?) = Query(ios.queryStartingAfterValues(fieldValues.toList())) + internal actual fun _startAfter(snapshot: DocumentSnapshot) = Query(ios.queryStartingAfterDocument(snapshot.ios)) + + internal actual fun _endAt(vararg fieldValues: Any?) = Query(ios.queryEndingAtValues(fieldValues.toList())) + internal actual fun _endAt(snapshot: DocumentSnapshot) = Query(ios.queryEndingAtDocument(snapshot.ios)) + internal actual fun _endBefore(vararg fieldValues: Any?) = Query(ios.queryEndingBeforeValues(fieldValues.toList())) + internal actual fun _endBefore(snapshot: DocumentSnapshot) = Query(ios.queryEndingBeforeDocument(snapshot.ios)) + } @Suppress("UNCHECKED_CAST") actual class CollectionReference(override val ios: FIRCollectionReference) : Query(ios) { diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index bc537ea1c..6ab854fd2 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -372,6 +372,16 @@ actual open class Query(open val js: firebase.firestore.Query) { Query(js.orderBy(field.js, direction.jsString)) } + internal actual fun _startAt(vararg fieldValues: Any?) = Query(js) // TODO: Correct implementation + internal actual fun _startAt(snapshot: DocumentSnapshot) = Query(js) // TODO: Correct implementation + internal actual fun _startAfter(vararg fieldValues: Any?) = Query(js) // TODO: Correct implementation + internal actual fun _startAfter(snapshot: DocumentSnapshot) = Query(js) // TODO: Correct implementation + + internal actual fun _endAt(vararg fieldValues: Any?) = Query(js) // TODO: Correct implementation + internal actual fun _endAt(snapshot: DocumentSnapshot) = Query(js) // TODO: Correct implementation + internal actual fun _endBefore(vararg fieldValues: Any?) = Query(js) // TODO: Correct implementation + internal actual fun _endBefore(snapshot: DocumentSnapshot) = Query(js) // TODO: Correct implementation + actual val snapshots get() = callbackFlow { val unsubscribe = rethrow { js.onSnapshot( From b93c124a99599107d4ffe77abb55ed375a4faea5 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Mon, 13 Dec 2021 18:02:57 -0700 Subject: [PATCH 12/13] Fixed endBefore name --- .../kotlin/dev/gitlive/firebase/firestore/firestore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index f5fc2b7c7..804df0178 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -140,8 +140,8 @@ fun Query.startAfter(snapshot: DocumentSnapshot) = _startAfter(snapshot) fun Query.endAt(vararg fieldValues: Any?) = _endAt(*fieldValues.map { encode(it) }.toTypedArray()) fun Query.endAt(snapshot: DocumentSnapshot) = _endAt(snapshot) -fun Query.endAfter(vararg fieldValues: Any?) = _endBefore(*fieldValues.map { encode(it) }.toTypedArray()) -fun Query.endAfter(snapshot: DocumentSnapshot) = _endBefore(snapshot) +fun Query.endBefore(vararg fieldValues: Any?) = _endBefore(*fieldValues.map { encode(it) }.toTypedArray()) +fun Query.endBefore(snapshot: DocumentSnapshot) = _endBefore(snapshot) expect class WriteBatch { inline fun set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): WriteBatch From 17837a1eeedf9a99af3005cb8c6961df03cbe991 Mon Sep 17 00:00:00 2001 From: Justin Rajewski Date: Thu, 13 Jan 2022 12:49:42 -0700 Subject: [PATCH 13/13] Updated iOS exceptions to print better --- build.gradle.kts | 9 ++++++--- firebase-app/package.json | 2 +- .../src/nativeInterop/cinterop/Cartfile | 2 +- firebase-auth/package.json | 2 +- .../kotlin/dev/gitlive/firebase/auth/auth.kt | 20 +++++++++---------- .../src/nativeInterop/cinterop/Cartfile | 2 +- firebase-common/package.json | 2 +- firebase-config/package.json | 2 +- .../src/nativeInterop/cinterop/Cartfile | 2 +- firebase-database/package.json | 2 +- .../src/nativeInterop/cinterop/Cartfile | 2 +- firebase-firestore/package.json | 2 +- .../gitlive/firebase/firestore/firestore.kt | 4 ++-- .../src/nativeInterop/cinterop/Cartfile | 2 +- firebase-functions/package.json | 2 +- .../src/nativeInterop/cinterop/Cartfile | 2 +- gradle.properties | 1 + 17 files changed, 32 insertions(+), 28 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fff5e4af1..17a86db1b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,7 +17,7 @@ buildscript { } } dependencies { - classpath("com.android.tools.build:gradle:7.0.1") + classpath("com.android.tools.build:gradle:7.0.4") classpath("com.adarshr:gradle-test-logger-plugin:2.1.1") } } @@ -49,6 +49,9 @@ subprojects { mavenLocal() google() mavenCentral() + maven { + setUrl("https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven") + } } tasks.withType().configureEach { @@ -195,9 +198,9 @@ subprojects { } dependencies { - "commonMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") + "commonMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-new-mm-dev2") "androidMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2") - "androidMainImplementation"(platform("com.google.firebase:firebase-bom:29.0.1")) + "androidMainImplementation"(platform("com.google.firebase:firebase-bom:29.0.3")) "commonTestImplementation"(kotlin("test-common")) "commonTestImplementation"(kotlin("test-annotations-common")) "commonTestImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") diff --git a/firebase-app/package.json b/firebase-app/package.json index b04ec1a26..be3cd96e5 100644 --- a/firebase-app/package.json +++ b/firebase-app/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-app", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-app.js", "scripts": { diff --git a/firebase-app/src/nativeInterop/cinterop/Cartfile b/firebase-app/src/nativeInterop/cinterop/Cartfile index 8d78efb90..e597e478e 100644 --- a/firebase-app/src/nativeInterop/cinterop/Cartfile +++ b/firebase-app/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 8.10.0 diff --git a/firebase-auth/package.json b/firebase-auth/package.json index e2668ff1f..88027f819 100644 --- a/firebase-auth/package.json +++ b/firebase-auth/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-auth", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-auth.js", "scripts": { diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 96e5a152e..7fd66258b 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -175,9 +175,9 @@ internal suspend inline fun T.await(function: T.(callback: (NSError?) -> Uni private fun NSError.toException() = when(domain) { FIRAuthErrorDomain -> when(code) { FIRAuthErrorCodeInvalidActionCode, - FIRAuthErrorCodeExpiredActionCode -> FirebaseAuthActionCodeException(toString()) + FIRAuthErrorCodeExpiredActionCode -> FirebaseAuthActionCodeException(localizedDescription) - FIRAuthErrorCodeInvalidEmail -> FirebaseAuthEmailException(toString()) + FIRAuthErrorCodeInvalidEmail -> FirebaseAuthEmailException(localizedDescription) FIRAuthErrorCodeCaptchaCheckFailed, FIRAuthErrorCodeInvalidPhoneNumber, @@ -187,26 +187,26 @@ private fun NSError.toException() = when(domain) { FIRAuthErrorCodeMissingVerificationID, FIRAuthErrorCodeMissingVerificationCode, FIRAuthErrorCodeWeakPassword, - FIRAuthErrorCodeInvalidCredential -> FirebaseAuthInvalidCredentialsException(toString()) + FIRAuthErrorCodeInvalidCredential -> FirebaseAuthInvalidCredentialsException(localizedDescription) - FIRAuthErrorCodeInvalidUserToken -> FirebaseAuthInvalidUserException(toString()) + FIRAuthErrorCodeInvalidUserToken -> FirebaseAuthInvalidUserException(localizedDescription) - FIRAuthErrorCodeRequiresRecentLogin -> FirebaseAuthRecentLoginRequiredException(toString()) + FIRAuthErrorCodeRequiresRecentLogin -> FirebaseAuthRecentLoginRequiredException(localizedDescription) FIRAuthErrorCodeSecondFactorAlreadyEnrolled, FIRAuthErrorCodeSecondFactorRequired, FIRAuthErrorCodeMaximumSecondFactorCountExceeded, - FIRAuthErrorCodeMultiFactorInfoNotFound -> FirebaseAuthMultiFactorException(toString()) + FIRAuthErrorCodeMultiFactorInfoNotFound -> FirebaseAuthMultiFactorException(localizedDescription) FIRAuthErrorCodeEmailAlreadyInUse, FIRAuthErrorCodeAccountExistsWithDifferentCredential, - FIRAuthErrorCodeCredentialAlreadyInUse -> FirebaseAuthUserCollisionException(toString()) + FIRAuthErrorCodeCredentialAlreadyInUse -> FirebaseAuthUserCollisionException(localizedDescription) FIRAuthErrorCodeWebContextAlreadyPresented, FIRAuthErrorCodeWebContextCancelled, - FIRAuthErrorCodeWebInternalError -> FirebaseAuthWebException(toString()) + FIRAuthErrorCodeWebInternalError -> FirebaseAuthWebException(localizedDescription) - else -> FirebaseAuthException(toString()) + else -> FirebaseAuthException(localizedDescription) } - else -> FirebaseAuthException(toString()) + else -> FirebaseAuthException(localizedDescription) } diff --git a/firebase-auth/src/nativeInterop/cinterop/Cartfile b/firebase-auth/src/nativeInterop/cinterop/Cartfile index c9d68bb30..b5c7b0dbf 100644 --- a/firebase-auth/src/nativeInterop/cinterop/Cartfile +++ b/firebase-auth/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" == 8.10.0 diff --git a/firebase-common/package.json b/firebase-common/package.json index 96cfb72d1..adc5d52c0 100644 --- a/firebase-common/package.json +++ b/firebase-common/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-common", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-common.js", "scripts": { diff --git a/firebase-config/package.json b/firebase-config/package.json index a1a774ef7..6e1800218 100644 --- a/firebase-config/package.json +++ b/firebase-config/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-config", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-config.js", "scripts": { diff --git a/firebase-config/src/nativeInterop/cinterop/Cartfile b/firebase-config/src/nativeInterop/cinterop/Cartfile index a944bdb12..59931a945 100644 --- a/firebase-config/src/nativeInterop/cinterop/Cartfile +++ b/firebase-config/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" == 8.10.0 diff --git a/firebase-database/package.json b/firebase-database/package.json index a18360057..2a2121d1f 100644 --- a/firebase-database/package.json +++ b/firebase-database/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-database", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-database.js", "scripts": { diff --git a/firebase-database/src/nativeInterop/cinterop/Cartfile b/firebase-database/src/nativeInterop/cinterop/Cartfile index 48e590b2d..7474f4014 100644 --- a/firebase-database/src/nativeInterop/cinterop/Cartfile +++ b/firebase-database/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" == 8.10.0 diff --git a/firebase-firestore/package.json b/firebase-firestore/package.json index 4583702a3..045a98451 100644 --- a/firebase-firestore/package.json +++ b/firebase-firestore/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-firestore", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-firestore.js", "scripts": { diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 3fcd09ec2..3c0b9b305 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -312,7 +312,7 @@ actual open class Query(open val ios: FIRQuery) { internal actual fun _where(path: FieldPath, inArray: List?, arrayContainsAny: List?) = Query( (inArray?.let { ios.queryWhereFieldPath(path.ios, `in` = it) } ?: ios).let { ios2 -> - arrayContainsAny?.let { ios2.queryWhereFieldPath(path.ios, arrayContainsAny = arrayContainsAny) } ?: ios2 + arrayContainsAny?.let { ios2.queryWhereFieldPath(path.ios, arrayContainsAny = arrayContainsAny) } ?: ios } ) @@ -408,7 +408,7 @@ fun NSError.toException() = when(domain) { else -> FirestoreExceptionCode.UNKNOWN } else -> FirestoreExceptionCode.UNKNOWN -}.let { FirebaseFirestoreException(description!!, it) } +}.let { FirebaseFirestoreException(localizedDescription!!, it) } actual class QuerySnapshot(val ios: FIRQuerySnapshot) { actual val documents diff --git a/firebase-firestore/src/nativeInterop/cinterop/Cartfile b/firebase-firestore/src/nativeInterop/cinterop/Cartfile index d8580d2e9..490990031 100644 --- a/firebase-firestore/src/nativeInterop/cinterop/Cartfile +++ b/firebase-firestore/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json" == 8.10.0 diff --git a/firebase-functions/package.json b/firebase-functions/package.json index a5ec276fb..2bbf9a8cb 100644 --- a/firebase-functions/package.json +++ b/firebase-functions/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-functions", - "version": "1.0.0", + "version": "1.4.3", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-functions.js", "scripts": { diff --git a/firebase-functions/src/nativeInterop/cinterop/Cartfile b/firebase-functions/src/nativeInterop/cinterop/Cartfile index 04b616b81..9ba45ee51 100644 --- a/firebase-functions/src/nativeInterop/cinterop/Cartfile +++ b/firebase-functions/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json" == 8.8.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json" == 8.10.0 diff --git a/gradle.properties b/gradle.properties index dead1d414..d7c1f3f27 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,7 @@ kotlin.js.experimental.generateKotlinExternals=false #kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.stability.nowarn=true kotlin.native.cacheKind.iosX64=none +kotlin.native.binary.memoryModel=experimental #kotlin.native.enableDependencyPropagation=false kotlin.native.enableParallelExecutionCheck=false kotlin.setJvmTargetFromAndroidCompileOptions=true