firebase / firebase/firebase-ios-sdk
Round-tripped Firestore Timestamp fields have altered nanoseconds
- Dominant language
- C++
- Stars
- 6.7k
- Forks
- 1.8k
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 75
Description
### Description
**Expected Result:**
When round-tripping a `Timestamp` through Firestore, it is exactly the same before and after.
**Actual Result:**
The nanosecond field of the `Timestamp` is consistently different.
**Why Is This A Problem:**
It makes data consistency checking very difficult. It may result in spurious change notifications.
### Reproducing the issue
This simple XCTest demonstrates the problem:
```swift
struct TestStruct: Codable, Hashable {
let timestamp: Timestamp
}
func testTimestampRoundtrip() async throws {
let id = UUID().uuidString
let initial = TestStruct(timestamp: .init(date: Date.now))
try myCollection.document(id).setData(from: initial)
let roundTripped = try await myCollection.document(id).getDocument(as: TestStruct.self)
XCTAssertEqual(initial, roundTripped)
}
```
The assert will fail with an error like:
```
XCTAssertEqual failed:
("TestStruct(timestamp: )")
is not equal to
("TestStruct(timestamp: )")
```
Note that my test framework is configured to hit a local Firebase emulator instead of the real infrastructure - I don't know if that makes a difference. I also tried specifying `cache` as the source for `getDocument(as:)`, but that didn't make a difference.
This does not appear to be a problem with encoding and decoding, because this test works just fine:
```swift
func testTimestampCoding() throws {
let initial = TestStruct(timestamp: .init(date: Date.now))
let encoder = Firestore.Encoder()
let encoded = try encoder.encode(initial)
let decoder = Firestore.Decoder()
let decoded = try decoder.decode(TestStruct.self, from: encoded)
XCTAssertEqual(initial, decoded)
}
```
### Firebase SDK Version
11.7.0
### Xcode Version
16.2
### Installation Method
Swift Package Manager
### Firebase Product(s)
Firestore
### Targeted Platforms
iOS
### Relevant Log Output
```shell
```
### If using Swift Package Manager, the project's Package.resolved
Expand Package.resolved snippet
```json
{
"originHash" : "279a5cbd0a3b875be5bd9e97c69521ee23b546261f86798aaaf0e3bdbbebfc53",
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27",
"version" : "1.2024011602.0"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
"version" : "11.2.0"
}
},
{
"identity" : "cocoalumberjack",
"kind" : "remoteSourceControl",
"location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
"state" : {
"revision" : "4b8714a7fb84d42393314ce897127b3939885ec3",
"version" : "3.8.5"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "0d885d28250fb1196b614bc9455079b75c531f72",
"version" : "11.7.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "be0881ff728eca210ccb628092af400c086abda3",
"version" : "11.7.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
"version" : "10.1.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb",
"version" : "8.0.2"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "f56d8fc3162de9a498377c7b6cea43431f4f5083",
"version" : "1.65.1"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "85b7b231882c3c472b8bda4fb495324d3f19bab6",
"version" : "4.2.0"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
"version" : "100.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log",
"state" : {
"revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91",
"version" : "1.6.2"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "ebc7251dd5b37f627c93698e4374084d98409633",
"version" : "1.28.2"
}
}
],
"version" : 3
}
```
### If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock snippet
```yml
Replace this line with the contents of your Podfile.lock!
```
Contributor guide
Assessment
This issue has not been assessed yet.