Skip to content

Conversation

@mortenbekditlevsen
Copy link
Contributor

@mortenbekditlevsen mortenbekditlevsen commented Mar 16, 2022

Continuing the discussion from PR #8858 here.

This PR makes Firestore use the FirebasesDataEncoder and FirebaseDataDecoder - bringing key coding strategies, etc. to the encoding, while attempting to be compatible with the existing encoder and decoder.

After restructuring of Firestore and FirebaseSharedSwift it was too hard for me to merge into the branch from #8858.

I still need to implement tests - since #8858 I have learned how to run tests by @peterfriese - thanks! :-)
I have a feeling that the CodablePassthroughTypes isn't working as it is being passed type erased values. Needs to be done through is casting/checking instead.

Internal API review at go/firestore-data-encoding-strategies-api

@paulb777 paulb777 added this to the Firebase 9 milestone Mar 20, 2022
Copy link
Member

@paulb777 paulb777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great to remove so many lines of code. thanks!

Do you need help resolving the PassthroughType related CI failures?

@mortenbekditlevsen
Copy link
Contributor Author

It's great to remove so many lines of code. thanks!

Do you need help resolving the PassthroughType related CI failures?

That would be great - I assume they are due to using a fixed version of FirebaseSharedSwift rather than the one in the same release? At least I had to add a reference to the local version of FirebaseSharedSwift in Firestore/Example/Podfile - but I don't know how all the other tests are built.

There are currently two failing tests in FirestoreEncoderTests, namely testDate and testDecodingDocumentIDWithConfictingFieldsThrows.

I think that the behavior of Date encoding and decoding ought to be discussed a bit more.
I.e. I think that the default in the current Firebase encoder is to pass through dates directly - and while decoding there are a few special cases like Timestamps being able to be decoded directly to Date... I don't know if all these behaviors should be recreated.

With regards to throwing at conflicting document id fields, I think that I mentioned on the other PR that I think that this is a quite harsh behavior that makes it hard to evolve your models towards using document ids or away from them - if old data models with old ids make your code crash. So I'd recommend that this behavior was removed on purpose.

@paulb777
Copy link
Member

@mortenbekditlevsen See #9493 for CI fixes

@peterfriese @ryanwilson @schmidt-sebastian Any thoughts about Morten's other questions?

@mortenbekditlevsen
Copy link
Contributor Author

To clarify my uncertainty about Date encoding and decoding:
I am not 100% certain about the current default encoding behavior for a Date.
By reading the code alone, it appears that the current encoder leaves it as a Date and lets the firestore serialization deal with it.
I just tried a small experiment, and it would appear to me that a Date serializes to a double_value field when written to firestore.
Does this mean that Date currently doesn't round-trip?

If this is true, the behavior is asymmetric with the decoding, where at least it appears that a Timestamp in Firestore will default to decoding to a Date. This behavior is why I added the timestamp date decoding strategy with a fallback, so that Timestamp always decodes to Date, but secondarily any other strategy applies.

I would suggest that the default encoding strategy of a Date was .timestamp, so that the behavior is more symmetric.

There's of course a whole other discussion about why Timestamp exists at all. Isn't the platforms default currency Date precise enough? :-)

@paulb777
Copy link
Member

Thanks @mortenbekditlevsen This Date discussion maps to these current CI test failures:

Failing tests:
Firestore_IntegrationTests_iOS:
FirestoreEncoderTests.testDate()
FirestoreEncoderTests.testDecodingDocumentIDWithConfictingFieldsThrows()

@mortenbekditlevsen
Copy link
Contributor Author

The first one, yes.
But it appears that I am mistaken. Date actually does round trip on master. I will have to look closer into what's going on.

The second failure is related to my question about whether the presence of eg an 'id' property in data decoded with a DocumentID called 'id' should actually fail.

I think that it should not because it complicates evolution of your models. So I think that test case should just be deleted.

@mortenbekditlevsen
Copy link
Contributor Author

Ok, I found out that my initial testing of the Date serialization in Firestore was wrong. A Date serialized to Firestore does indeed become a Timestamp when reading it back.
I suggest that the default date encoding strategy then should just be .timestamp. This means that it becomes a Timestamp already upon encoding (before serialization), but the net result is the same.
I'll go ahead and make that change and update the test to reflect that.

@schmidt-sebastian
Copy link
Contributor

Sorry, I was out of office for a couple of days. Looks like you were able to find answers to your questions.

Firestore accepts Dates and Timestamps as input but by default only returns Timestamps. For customers that use Codable, we can deviate from the default when we know that the resulting type is meant to be a Date. This still seems to be the case if I understand testTimestampCanDecodeAsDate correctly. FWIW, Firestore TImestamps supports microsecond precision.

I don't have a strong opinion in regards to the DoumcentID validation. I agree that it makes it harder to migrate data models, so it seems reasonable to remove this validation.

@paulb777
Copy link
Member

paulb777 commented Apr 4, 2022

What is outstanding on this PR besides a CHANGELOG update? @mortenbekditlevsen @peterfriese @schmidt-sebastian @ryanwilson

@mortenbekditlevsen
Copy link
Contributor Author

Hi Paul,
There's one thing that I would like to change. I think that I 'over engineered' the .timestamp(fallback) date decoding strategy.
The idea was, that since the existing Firestore Decoder always succeeds when decoding a Timestamp into a model containing a Date, then this always should be kept for backwards compatibility. So I made a timestamp(fallback) date decoding strategy that first sees if the encoded data is a Timestamp - and if it is, then it returns it's requested Date representation. And otherwise it falls back to the decoding strategy present in the argument...

But actually the always in the current decoder only needs to translate to 'by default'. Meaning that the default date decoding strategy for the Firestore decoder would just be a 'timestamp' decoder that looks like:

extension Firestore.Decoder.DateDecodingStrategy {
  /// Decode the `Date` from a Firestore `Timestamp`
  static var timestamp: Firestore.Decoder.DateDecodingStrategy {
    return .custom { decoder in
      let container = try decoder.singleValueContainer()
      let value = try container.decode(Timestamp.self)
      return value.dateValue()
    }
  }
}

This doesn't mean that the fallback behavior that I already implemented can't be of value, but it complicates the API a bit and it can of course be implemented by the user if one really wishes that behavior.

What do you think? Should I go ahead and do the simpler timestamp date decoding strategy or keep the version that's a bit more involved?

@paulb777
Copy link
Member

paulb777 commented Apr 6, 2022

@mortenbekditlevsen It sounds like you're saying that there is no difference in behavior between the two implementations. In that case, it sounds good to me to go ahead and update to the simpler implementation.

@mortenbekditlevsen
Copy link
Contributor Author

@mortenbekditlevsen It sounds like you're saying that there is no difference in behavior between the two implementations. In that case, it sounds good to me to go ahead and update to the simpler implementation.

Well, not exactly, but the default behavior (and thus the compatibility with previous versions) is the same and I don't think there's a whole lot of value in the more complex behavior.
I'll go ahead and commit the simpler version, and it can be compared to the more complex implementation through the git history if anyone is interested. :-)

@paulb777
Copy link
Member

paulb777 commented Apr 7, 2022

Thanks @mortenbekditlevsen! That looks much more maintainable.

Would you resolve the conflict with the master branch?

@mortenbekditlevsen
Copy link
Contributor Author

Sure, I'll take a look at it later today.

@paulb777 paulb777 modified the milestones: 9.2.0 - M117, 9.3.0 - M118 Jun 15, 2022
@ryanwilson ryanwilson modified the milestones: 9.3.0 - M118, 9.4.0 - M119 Jul 7, 2022
@charlotteliang charlotteliang removed this from the 9.4.0 - M119 milestone Jul 26, 2022
@mortenbekditlevsen
Copy link
Contributor Author

Hi @chliangGoogle ,
I just noticed that the milestone for this PR was previously moved forward, but 16 days ago it was removed entirely.
Does that mean that it's 'off the radar' for getting merged - or is the review just taking longer than expected?

@charlotteliang charlotteliang added this to the 9.5.0 - M120 milestone Aug 11, 2022
@charlotteliang
Copy link
Contributor

@mortenbekditlevsen I think we just forget to move this to the next release as we are releasing the previous release.

@paulb777
Copy link
Member

@mortenbekditlevsen Sorry that we've been so slow at landing this PR. I'd really like to get it in before our code freeze deadline for Firebase 10.0.0 in the next week or so.

Reading through the comments, I see a few open questions, but I'm not sure what exactly still needs to be resolved to merge. Given that 10.0.0 is a breaking change release, we can allow some breakages if we document them and believe they're only used in corner cases.

Is it possible to update the Firestore CHANGELOG that summarizes the implications for the API's clients?

@mortenbekditlevsen
Copy link
Contributor Author

I'll have a look at it tomorrow evening. There may not be any breaking changes after all. I'll have a look and suggest an addition to the change log.

@mortenbekditlevsen
Copy link
Contributor Author

mortenbekditlevsen commented Sep 18, 2022

I added the following to the CHANGELOG - please let me know if you think it requires more details:

10.0.0

  • [changed] Firestore.Encoder and Firestore.Decoder now wraps the shared FirebaseDataEncoder and FirebaseDataDecoder types which provides new customization options for encoding and decoding date to and from Firestore - similar to the options present on JSONEncoder and JSONDecoder from Foundation.
  • [added] Firestore.Encoder.KeyEncodingStrategy
  • [added] Firestore.Encoder.DateEncodingStrategy
  • [added] Firestore.Encoder.DataEncodingStrategy
  • [added] Firestore.Encoder.NonConformingFloatEncodingStrategy
  • [added] Firestore.Decoder.KeyDecodingStrategy
  • [added] Firestore.Decoder.DateDecodingStrategy
  • [added] Firestore.Decoder.DataDecodingStrategy
  • [added] Firestore.Decoder.NonConformingFloatDecodingStrategy

@mortenbekditlevsen
Copy link
Contributor Author

With regards to breaking changes, I think that the way we ended up implementing it - by wrapping the shared encoder and decoder instead of just making it be a typealias to the shared encoder and decoder - ended up giving it a fully backwards compatible API surface. I'm not certain how to prove this, however. Isn't there some api checker tool that can verify this externally?

Copy link
Member

@paulb777 paulb777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mortenbekditlevsen. Would it feasible to add tests for the new customization options?

@mortenbekditlevsen
Copy link
Contributor Author

The various encoding and decoding strategies are tested thoroughly in FirebaseSharedSwift and the 'defaults' are tested through the existing tests. I could add some extra tests for the combination. This will verify that the wrapping of the decoder/encoder correctly passes on the strategies.
Is there a deadline for the release 10 work?

@paulb777
Copy link
Member

Thanks @mortenbekditlevsen. Those tests would be useful, although not necessarily blocking for this PR to merge. We're planning to code freeze for Firebase 10 on September 27.

@paulb777
Copy link
Member

The API review is still awaiting final approval, and I'm going to merge here to get into the Firebase 10 testing.

@paulb777 paulb777 merged commit 65f6cfa into firebase:master Sep 23, 2022
ncooke3 pushed a commit that referenced this pull request Sep 24, 2022
ncooke3 added a commit that referenced this pull request Sep 24, 2022
* [Firebase 10] Bump 9.X versions to 10.0.0

* Bump min. GTMSessionFetcher dep version to 2.1

* Bump min. GDT dep version to 9.2

* Bump min. GULs dep version to 7.8

* Fix unintentional nanopb replace

* Fix unintentional nanopb replace (2)

* Fix unintentional nanopb replace (3)

* Fix unintentional nanopb replace (4)

* Fix unintentional nanopb replace (4)

* Trigger all CI

* Bump FirebaseCombineSwift to 10

* Fix FirebaseStorage.podspec
I'm not sure what happened here. I had committed it during the rebase but guess not?

* Fix unintentional nanopb replace (5)

* Disable two tests to unblock staging

* Fix and re-enable FIROptions tests

* Update deployment targets in Firebase.podspec

* Add CHANGELOG entry for GTMSessionFetcher

* Fix AppCheck iOS availability

* Fix inadvertent API change (#10245)

* Docs update for Extensions and putFile (#10248)

* App google domain support (#10249)

Adding new 1p domain "app.google" support in FDL SDK.

* Remote Config Dynamic Property Wrapper (#10155)

* Separate GoogleUtilities Carthage build (#10250)

* Public count (#10246)

* Public Count

* Swift Test Commit

* Swift Format

* No extra whitespace

* Hopefully formatted.

* Change log and feedback.

* Revert "Public count (#10246)" (#10252)

This reverts commit 8aae6be.

* Make Firestore use FirebaseDataEncoder and FirebaseDataDecoder (re-implementation of #8858) (#9465)

* Bump tvOS minimum support version to 12.0

* [skip ci] Revert Gemfile

Co-authored-by: Paul Beusterien <paulbeusterien@google.com>
Co-authored-by: Eldhose M Babu <eldhosembabu@google.com>
Co-authored-by: Charlotte Liang <chliang@google.com>
Co-authored-by: wu-hui <53845758+wu-hui@users.noreply.github.com>
Co-authored-by: Morten Bek Ditlevsen <bek@termestrup.dk>
@@ -1,3 +1,14 @@
# 10.0.0
- [changed] `Firestore.Encoder` and `Firestore.Decoder` now wraps the shared `FirebaseDataEncoder` and `FirebaseDataDecoder` types which provides new customization options for encoding and decoding date to and from Firestore - similar to the options present on `JSONEncoder` and `JSONDecoder` from `Foundation`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "options for encoding and decoding date" date --> data or data types?

@@ -1,3 +1,14 @@
# 10.0.0
- [changed] `Firestore.Encoder` and `Firestore.Decoder` now wraps the shared `FirebaseDataEncoder` and `FirebaseDataDecoder` types which provides new customization options for encoding and decoding date to and from Firestore - similar to the options present on `JSONEncoder` and `JSONDecoder` from `Foundation`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this be line wrapped?

@firebase firebase locked and limited conversation to collaborators Oct 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants