firebase-kotlin-sdk icon indicating copy to clipboard operation
firebase-kotlin-sdk copied to clipboard

Support Firestore Timestamps for all platforms

Open andersio opened this issue 5 years ago • 8 comments
trafficstars

Library Class Member Platforms
Firestore (iOS)
com.google.firebase (Android)
FIRTimestamp (iOS)
Timestamp (Android)
N/A All platforms

There doesn't seem a way to reliably deserialise FIRTimestamp or com.google.firebase.Timestamp. Given this simple stub:

        val o1 = NSMutableDictionary()
        o1.setObject( FIRTimestamp(1, 2), forKey = "test" as NSString)

        val o2 = decode(TestData.serializer(), o1)

        print("@@@ $o2")

I've attempted to deserialise it with the decode function in dev.gitlive:firebase-common:

  1. Decode as string

    @Serializable
    data class TestData(val test: String)
    
  2. Decode as data class

    @Serializable
    data class TestData(val test: TestTimestamp)
    
    @Serializable
    data class TestTimestamp(val seconds: Long, val nanoseconds: Int)
    

(1) works, but the resulting String is coming from -[NSObject description], which is not guaranteed to be a stable string representation. The format might also differ on different platforms.

(2) does not work, since it crashes when attempting to cast the ObjC FIRTimestamp object to a Map<*, *>.

Presumably this is the cause: https://github.com/GitLiveApp/firebase-kotlin-sdk/blob/master/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt#L14

Given that

  1. (AFAIK) FIRTimestamp (or its Android equivalent) is the only exception
  2. how the decoder contract of kotlinx.serialization is designed

So it seems it is a reasonable option to ad a special path for FIRTimestamp in the said code path, so as to enable (2).

I'd love to contribute on this, so please let me know if this makes sense.

andersio avatar Jul 22 '20 13:07 andersio

Oh yes, I'm also very much interested this.

vanniktech avatar Jul 22 '20 13:07 vanniktech

OK, yes we should probably support both decoding to a double/long and Timestamp class. For the Timestamp class we would need to create an expect class Timestamp to common layer

Happy to receive a PR on this @andersio

nbransby avatar Jul 22 '20 14:07 nbransby

Probably should consider the kotlinx-datetime lib too.

CoreyKaylor avatar Sep 19 '20 23:09 CoreyKaylor

We could make very good use of a fix to this issue, too. Experiencing the same problem.

ansgarkonermann avatar Nov 25 '20 12:11 ansgarkonermann

OK, yes we should probably support both decoding to a double/long and Timestamp class. For the Timestamp class we would need to create an expect class Timestamp to common layer

I would love to contribute. Could you elaborate on that? Decoding and Encoding is implemented in firebase-common, wich firebase-firestore depends on. So how could we decode GeoPoint and Timestamp whose expect and actual classes are then defined in firebase-firestore?

jonatbergn avatar Nov 27 '20 08:11 jonatbergn

@jonatbergn we would need to just wrap the native types received from the firebase SDKs with our expect/actual version. We would check the expected type during decode and assume we have the native type and wrap it.

Start by creating expect/actuals for GeoPoint and Timestamp in the same pattern we have for other classes

nbransby avatar Dec 30 '20 12:12 nbransby

I was hoping to tackle this problem and after spending a few hours getting familiar with this library I'm not sure how to proceed.

It's my understanding that whatever we try to write gets sent through the serializer which breaks it down into primitives. The primitives are then encoded into lists and maps and passed to the native APIs.

It seems like any Timestamp class will be broken down into the primitives without any way to detect it at the encoding stage. I was thinking it could be possible to serialize it in a way that the encoder could rebuild it but this would require firebase-common to have knowledge of the Timestamp class and depend on firebase-firestore. The other option is to define the Timestamp class in the firebase-common package but this would make it depend on the firestore API.

I'm going to try the second approach as I don't care if it also depends on the firestore API as I already have that in my project.

EDIT: I realized even if I did this and managed to get it to write a Timestamp to firestore I have no idea how you'd decode one.

litclimbing avatar Nov 18 '21 20:11 litclimbing

I now have a working version tested on Android only so far https://github.com/litclimbing/firebase-kotlin-sdk/tree/add-timestamp

My approach was to create specialized encoder/decoders and provide dummy serializers for the special types. The encoder/decoder look for the special types in the encode/decodeSerializableValue functions and does the translation there.

I also removed all the positive infinity double stuff used as a flag for the server timestamps. The FieldValues are recognized by the encoder and passed through.

Currently, I've only added support for firestore. The database module will need its own support added.

litclimbing avatar Nov 20 '21 16:11 litclimbing

I believe this has been added in 1.9.0 - please reopen if something is missing

nbransby avatar Sep 02 '23 21:09 nbransby