Skip to content

Commit 791e43a

Browse files
committed
[realppl 10] Add server timestamp support
1 parent c33f64a commit 791e43a

File tree

14 files changed

+515
-53
lines changed

14 files changed

+515
-53
lines changed

Firestore/Source/Public/FirebaseFirestore/FIRPipelineBridge.h

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

229229
- (nullable id)get:(id)field;
230230

231+
- (nullable id)get:(id)field
232+
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior;
233+
231234
@end
232235

233236
NS_SWIFT_SENDABLE

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,38 @@ 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().mapValues { Helper.convertObjCToSwift($0) }
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+
47+
init(_ bridge: __PipelineResultBridge, _ behavior: ServerTimestampBehavior) {
48+
self.bridge = bridge
49+
serverTimestamp = behavior
50+
ref = self.bridge.reference
51+
id = self.bridge.documentID
52+
data = self.bridge.data(with: serverTimestamp)
53+
createTime = self.bridge.create_time
54+
updateTime = self.bridge.update_time
55+
}
56+
3557
/// The reference of the document, if the query returns the `__name__` field.
3658
public let ref: DocumentReference?
3759

@@ -51,20 +73,20 @@ public struct PipelineResult: @unchecked Sendable {
5173
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
5274
/// - Returns: The data at the specified field location or `nil` if no such field exists.
5375
public func get(_ fieldName: String) -> Sendable? {
54-
return Helper.convertObjCToSwift(bridge.get(fieldName))
76+
return Helper.convertObjCToSwift(bridge.get(fieldName, serverTimestampBehavior: serverTimestamp))
5577
}
5678

5779
/// Retrieves the field specified by `fieldPath`.
5880
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
5981
/// - Returns: The data at the specified field location or `nil` if no such field exists.
6082
public func get(_ fieldPath: FieldPath) -> Sendable? {
61-
return Helper.convertObjCToSwift(bridge.get(fieldPath))
83+
return Helper.convertObjCToSwift(bridge.get(fieldPath, serverTimestampBehavior: serverTimestamp))
6284
}
6385

6486
/// Retrieves the field specified by `fieldPath`.
6587
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
6688
/// - Returns: The data at the specified field location or `nil` if no such field exists.
6789
public func get(_ field: Field) -> Sendable? {
68-
return Helper.convertObjCToSwift(bridge.get(field.fieldName))
90+
return Helper.convertObjCToSwift(bridge.get(field.fieldName, serverTimestampBehavior: serverTimestamp))
6991
}
7092
}

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)