alickbass / CodableFirebase

Use Codable with Firebase
MIT License
692 stars 91 forks source link

How to map to FIRTimestamp #36

Open cerupcat opened 6 years ago

cerupcat commented 6 years ago

I'm trying to figure out how map to FIRTimestamp since it's not codable currently. Is there a way to transform the data returned from FIRTimestamp to Date instead or make FIRTimestamp codable so that it maps correctly?

dannypier commented 6 years ago

Struggling with this as well as Firestore has now made the push to FIRTimestamp and .dateValue. Would be happy to do a PR for the feature if I could get a little guidance.

dannypier commented 6 years ago

@cerupcat I would not use this in production, but here's a hack I was able to get FIRTimestamps working with. Again... it's a HACK, be forewarned.

https://github.com/alickbass/CodableFirebase/pull/42

cerupcat commented 6 years ago

Thanks, i'll take a look.

alickbass commented 6 years ago

Hey guys! First of all, sorry for being unresponsive lately... I am really glad that there is support from community!

About the issue. I think that the proper way to do it, would be to do something similar to here. So we can make FirTimestamp Encodable and it will be skipped and left untouched when encoding and thus will let the Firestore handle it. I will open a PR with an example of how to use it! What do you think about it?

jonandersen commented 6 years ago

@alickbass would be great if we get some best practice guidance on that!

alickbass commented 6 years ago

Hey guys! The PR makes Timestamp conform to Codable and you can use it directly in your code just like normal Date. Something like below:

import CodableFirebase

extension Timestamp: TimestampType {}

struct FirTimestampModel: Codable {
  let timestamp: Timestamp
}

let timestamp = FirTimestampModel(timestamp: Timestamp(date: Date()))
let data = try! FirestoreEncoder().encode(timestamp)

If you want to use Date in your models and convert to Timestamp on server, I would really discourage that, as then you will lose all the precision.

luca251117 commented 6 years ago

Thank you for your effort 👍

dannypier commented 6 years ago

Thanks @alickbass! Agree to disagree about having Timestamp in your models though. Creating a dependency on Firestore for the Timestamp type at the model layer doesn't feel good to me.

alickbass commented 6 years ago

Well, I think that the reason to use FirTimestamp in the first place is that you are not satisfied with the precision of the Date. Why not use Date directly then?

dannypier commented 6 years ago

Yeah, I would prefer to use Date directly but a recent version of Firestore told (via a console message) that the using of Date was going to be deprecated and the correct thing to do was get dateValue of FIRTimestamp. That's actually the thing that got me involved in this issue, precision wasn't so much of a concern.

The behavior for system Date objects stored in Firestore is going to change AND YOUR APP MAY BREAK.
To hide this warning and ensure your app does not break, you need to add the following code to your app before calling any other Cloud Firestore methods:

let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings

With this change, timestamps stored in Cloud Firestore will be read back as Firebase Timestamp objects instead of as system Date objects. So you will also need to update code expecting a Date to instead expect a Timestamp. For example:

// old:
let date: Date = documentSnapshot.get("created_at") as! Date
// new:
let timestamp: Timestamp = documentSnapshot.get("created_at") as! Timestamp
let date: Date = timestamp.dateValue()

Please audit all existing usages of Date when you enable the new behavior. In a future release, the behavior will be changed to the new behavior, so if you do not follow these steps, YOUR APP MAY BREAK.
alickbass commented 6 years ago

Valid point, @dannypier... Maybe I will find a better alternative... I will keep you updated, guys

serjooo commented 6 years ago

@alickbass Right now Firestore is crashing while using Date objects in the model, and I am also not very keen to be using FIRTimestamp in my model objects. It's not a dependency I'd like to have in my project as we might shift from Firestore in the future.

Edit: Firestore wasn't crashing because of the FIRTimestamp. Sorry, had a wrong call because of another Google product.

I took an attempt at supporting this. I realized the best way to do it is while encoding a Date object to convert it into FIRTimestamp with their initializer + (instancetype)timestampWithDate:(NSDate *)date; and while decoding if expecting a Date object taking the FIRTimestamp and doing timestamp.dateValue() and if expecting it as FIRTimestamp just return that

However, to achieve this CodableFirebase has to have access to FIRTimestamp in the project, that would mean we would need to add the Firestore pod into the project. I'm not sure how open you are to doing this.

alickbass commented 6 years ago

In general, I am okay with adding Firebase as a dependency and removing all the redundant protocols and just working with Firebase / Firestore values. If you guys are willing to contribute, please submit a PR and I would love to review. Unfortunately, I haven't had time lately to submit any code myself

serjooo commented 6 years ago

@alickbass I'll work on it this weekend. I was busy this past week. I'm going to try to do it without actually including Firebase as a dependency, if I am not able I'll just go ahead and add it.

alickbass commented 6 years ago

@serjooo tbh, I would like to add it as a dependency and remove all the weird protocols that we have.

serjooo commented 6 years ago

@alickbass What method do you see best fitting to add Firebase as a dependency? I stumbled upon this issue where things become a little tricky to add Firebase through Cocoapods

jimijon commented 5 years ago

Was this ever solved? @serjooo @alickbass

serjooo commented 5 years ago

@jimijon not yet. We need to add Firebase as a dependency of this project; however, I really had some hard time doing it and never had the time to research and give a shot again, but most probably I'll try to contribute again as I'm thinking of using CodableFirebase again for a future project of mine.

siyao1030 commented 5 years ago

@serjooo @alickbass any updates on this and/or is there any workaround? I had to update Firestore so I can use their new collection group query but keep running into the crash when decoding Date in the new version (Firebase 6.0.0, Firestore 1.3.0).

serjooo commented 5 years ago

@alickbass I finally found sometime to work on this today and I included the FirebaseFirestore pod as a dependency to CodableFirebase and started working on the Encoding and Decoding. However, I realized Google are almost ready to merge in your implementation and some changes on to master via this PR https://github.com/firebase/firebase-ios-sdk/pull/2229

Do you think its still worth investing more time and doing the same implementation as that PR on CodableFirebase and merge it until they release?

Also something to note adding FirebaseFirestore stops support for macOS watchOS and tvOS because the Podspec lint errored as FirebaseFirestore itself doesn't have official support for them

alickbass commented 5 years ago

I have started working on that PR myself quite some time ago, however, due to change in work could not continue... I am not sure what is their roadmap and timeline is...

serjooo commented 5 years ago

Yes you are right it seems like they keep postponing the release and you can never be sure of what their timeline is. How about you submit a PR of your current work on a separate branch and we can work on it together?

alickbass commented 5 years ago

sorry, I meant the PR on Firebase itself 😂I haven't done anything here yet, so feel free to submit your ideas 😎

SidPatel12 commented 5 years ago

let date: Date = timestamp.dateValue()

perfect one . it's woking for me. thank you so much for sharing

danismatiazGL commented 5 years ago

So, what happened with this ? I'm still dealing with

Expected to decode Date but found FIRTimestamp instead.

What do you guys suggest to solve this ? I have a Date object in my model

SidPatel12 commented 5 years ago

So, what happened with this ? I'm still dealing with

Expected to decode Date but found FIRTimestamp instead.

What do you guys suggest to solve this ? I have a Date object in my model } **

let timestamp: Timestamp = dicDocument["Date"] as! Timestamp let timeStmp = timestamp.dateValue() msg.date = timeStmp

** i have taken date variable and its type is Date() in model. i have stored date as timeStamp in firestore.

serjooo commented 5 years ago

@alickbass seems like Google merged Codable support into master https://github.com/firebase/firebase-ios-sdk/pull/3198 didn't check the implementation or test it out myself. Will do that and close this issue accordingly

Rspoon3 commented 4 years ago

So, what happened with this ? I'm still dealing with Expected to decode Date but found FIRTimestamp instead. What do you guys suggest to solve this ? I have a Date object in my model } **

let timestamp: Timestamp = dicDocument["Date"] as! Timestamp let timeStmp = timestamp.dateValue() msg.date = timeStmp

** i have taken date variable and its type is Date() in model. i have stored date as timeStamp in firestore.

How did you end up doing this?

Rspoon3 commented 4 years ago

@alickbass seems like Google merged Codable support into master firebase/firebase-ios-sdk#3198 didn't check the implementation or test it out myself. Will do that and close this issue accordingly

How does it compare to this library?

bryan1anderson commented 3 years ago

I'm running into this On my model I have these values:

    var created: Date?
    var updated: Date?

Firestore is returning them in this format: "updated": <FIRTimestamp: seconds=1617128658 nanoseconds=859000000>

My understanding is that I could use this library to decode from Timestamp directly to date like this:

let model = try FirestoreDecoder().decode(Model.self, from: item.data())

Unfortunately it throws the error: typeMismatch(Foundation.Date, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "created", intValue: nil)], debugDescription: "Expected to decode Date but found FIRTimestamp instead.", underlyingError: nil))

Does anyone know how to solve this? I'm pulling my hair out trying to avoid writing a custom initializer or even a custom model for the one area of the app where this model gets loaded up from the Firestore.