Skip to content

Commit 54eee56

Browse files
committed
[realppl 10] Add server timestamp support
1 parent 01df95f commit 54eee56

File tree

15 files changed

+509
-53
lines changed

15 files changed

+509
-53
lines changed

Firestore/Source/Public/FirebaseFirestore/FIRPipelineBridge.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ NS_SWIFT_NAME(__PipelineResultBridge)
223223

224224
- (nullable id)get:(id)field;
225225

226+
- (nullable id)get:(id)field
227+
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior;
228+
226229
@end
227230

228231
NS_SWIFT_SENDABLE

Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Constant.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ public struct Constant: Expr, BridgeWrapper, @unchecked Sendable {
3434
}
3535

3636
// Initializer for numbers
37+
public init(_ value: Int) {
38+
self.init(value as Any)
39+
}
40+
3741
public init(_ value: Double) {
3842
self.init(value as Any)
3943
}

Firestore/Swift/Source/SwiftAPI/Pipeline/PipelineResult.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,28 @@ import Foundation
2222
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
2323
public struct PipelineResult: @unchecked Sendable {
2424
let bridge: __PipelineResultBridge
25+
private let serverTimestamp: ServerTimestampBehavior
2526

2627
init(_ bridge: __PipelineResultBridge) {
2728
self.bridge = bridge
29+
serverTimestamp = .none
2830
ref = self.bridge.reference
2931
id = self.bridge.documentID
3032
data = self.bridge.data()
3133
createTime = self.bridge.create_time
3234
updateTime = self.bridge.update_time
3335
}
3436

37+
init(_ bridge: __PipelineResultBridge, _ behavior: ServerTimestampBehavior) {
38+
self.bridge = bridge
39+
serverTimestamp = behavior
40+
ref = self.bridge.reference
41+
id = self.bridge.documentID
42+
data = self.bridge.data(with: serverTimestamp)
43+
createTime = self.bridge.create_time
44+
updateTime = self.bridge.update_time
45+
}
46+
3547
/// The reference of the document, if the query returns the `__name__` field.
3648
public let ref: DocumentReference?
3749

@@ -51,20 +63,20 @@ public struct PipelineResult: @unchecked Sendable {
5163
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
5264
/// - Returns: The data at the specified field location or `nil` if no such field exists.
5365
public func get(_ fieldName: String) -> Sendable? {
54-
return bridge.get(fieldName)
66+
return bridge.get(fieldName, serverTimestampBehavior: serverTimestamp)
5567
}
5668

5769
/// Retrieves the field specified by `fieldPath`.
5870
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
5971
/// - Returns: The data at the specified field location or `nil` if no such field exists.
6072
public func get(_ fieldPath: FieldPath) -> Sendable? {
61-
return bridge.get(fieldPath)
73+
return bridge.get(fieldPath, serverTimestampBehavior: serverTimestamp)
6274
}
6375

6476
/// Retrieves the field specified by `fieldPath`.
6577
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
6678
/// - Returns: The data at the specified field location or `nil` if no such field exists.
6779
public func get(_ field: Field) -> Sendable? {
68-
return bridge.get(field.fieldName)
80+
return bridge.get(field.fieldName, serverTimestampBehavior: serverTimestamp)
6981
}
7082
}

Firestore/Swift/Source/SwiftAPI/Pipeline/RealtimePipeline.swift

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,6 @@ import Foundation
2121

2222
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
2323
public struct PipelineListenOptions: Sendable, Equatable, Hashable {
24-
/// Defines how to handle server-generated timestamps that are not yet known locally
25-
/// during latency compensation.
26-
public struct ServerTimestampBehavior: Sendable, Equatable, Hashable {
27-
/// The raw string value for the behavior, used for implementation and hashability.
28-
let rawValue: String
29-
30-
/// Creates a new behavior with a private raw value.
31-
private init(rawValue: String) {
32-
self.rawValue = rawValue
33-
}
34-
35-
/// Fields dependent on server timestamps will be `nil` until the value is
36-
/// confirmed by the server.
37-
public static let none = ServerTimestampBehavior(rawValue: "none")
38-
39-
/// Fields dependent on server timestamps will receive a local, client-generated
40-
/// time estimate until the value is confirmed by the server.
41-
public static let estimate = ServerTimestampBehavior(rawValue: "estimate")
42-
43-
/// Fields dependent on server timestamps will hold the value from the last
44-
/// server-confirmed write until the new value is confirmed.
45-
public static let previous = ServerTimestampBehavior(rawValue: "previous")
46-
}
47-
4824
// MARK: - Stored Properties
4925

5026
/// The desired behavior for handling pending server timestamps.
@@ -70,16 +46,31 @@ public struct PipelineListenOptions: Sendable, Equatable, Hashable {
7046
self.includeMetadataChanges = includeMetadataChanges
7147
self.source = source
7248
bridge = __PipelineListenOptionsBridge(
73-
serverTimestampBehavior: (self.serverTimestamps ?? .none).rawValue,
49+
serverTimestampBehavior: PipelineListenOptions
50+
.toRawValue(servertimestamp: self.serverTimestamps ?? .none),
7451
includeMetadata: self.includeMetadataChanges ?? false,
7552
source: self.source ?? ListenSource.default
7653
)
7754
}
55+
56+
private static func toRawValue(servertimestamp: ServerTimestampBehavior) -> String {
57+
switch servertimestamp {
58+
case .none:
59+
return "none"
60+
case .estimate:
61+
return "estimate"
62+
case .previous:
63+
return "previous"
64+
@unknown default:
65+
fatalError("Unknown server timestamp behavior")
66+
}
67+
}
7868
}
7969

8070
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
8171
public struct RealtimePipeline: @unchecked Sendable {
8272
private var stages: [Stage]
73+
8374
let bridge: RealtimePipelineBridge
8475
let db: Firestore
8576

@@ -93,14 +84,18 @@ public struct RealtimePipeline: @unchecked Sendable {
9384
listener: @escaping (RealtimePipelineSnapshot?, Error?) -> Void)
9485
-> ListenerRegistration {
9586
return bridge.addSnapshotListener(options: options.bridge) { snapshotBridge, error in
96-
listener(
97-
RealtimePipelineSnapshot(
98-
// TODO(pipeline): this needs to be fixed
99-
snapshotBridge!,
100-
pipeline: self
101-
),
102-
error
103-
)
87+
if snapshotBridge != nil {
88+
listener(
89+
RealtimePipelineSnapshot(
90+
snapshotBridge!,
91+
pipeline: self,
92+
options: options
93+
),
94+
error
95+
)
96+
} else {
97+
listener(nil, error)
98+
}
10499
}
105100
}
106101

Firestore/Swift/Source/SwiftAPI/Pipeline/RealtimePipelineSnapshot.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ public struct RealtimePipelineSnapshot: Sendable {
3131
public let metadata: SnapshotMetadata
3232

3333
let bridge: __RealtimePipelineSnapshotBridge
34+
private var options: PipelineListenOptions
3435

35-
init(_ bridge: __RealtimePipelineSnapshotBridge, pipeline: RealtimePipeline) {
36+
init(_ bridge: __RealtimePipelineSnapshotBridge,
37+
pipeline: RealtimePipeline,
38+
options: PipelineListenOptions) {
3639
self.bridge = bridge
3740
self.pipeline = pipeline
41+
self.options = options
3842
metadata = bridge.metadata
39-
results_cache = self.bridge.results.map { PipelineResult($0) }
43+
results_cache = self.bridge.results
44+
.map { PipelineResult($0, options.serverTimestamps ?? .none) }
4045
changes = self.bridge.changes.map { PipelineResultChange($0) }
4146
}
4247

0 commit comments

Comments
 (0)