Skip to content

[realppl 10] Add server timestamp support #15011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: wuandy/RealPpl_9
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Firestore/Source/Public/FirebaseFirestore/FIRPipelineBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ NS_SWIFT_NAME(__PipelineResultBridge)

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

- (nullable id)get:(id)field
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior;

@end

NS_SWIFT_SENDABLE
Expand Down
4 changes: 4 additions & 0 deletions Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Constant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public struct Constant: Expr, BridgeWrapper, @unchecked Sendable {
}

// Initializer for numbers
public init(_ value: Int) {
self.init(value as Any)
}

public init(_ value: Double) {
self.init(value as Any)
}
Expand Down
18 changes: 15 additions & 3 deletions Firestore/Swift/Source/SwiftAPI/Pipeline/PipelineResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,28 @@ import Foundation
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
public struct PipelineResult: @unchecked Sendable {
let bridge: __PipelineResultBridge
private let serverTimestamp: ServerTimestampBehavior

init(_ bridge: __PipelineResultBridge) {
self.bridge = bridge
serverTimestamp = .none
ref = self.bridge.reference
id = self.bridge.documentID
data = self.bridge.data()
createTime = self.bridge.create_time
updateTime = self.bridge.update_time
}

init(_ bridge: __PipelineResultBridge, _ behavior: ServerTimestampBehavior) {
self.bridge = bridge
serverTimestamp = behavior
ref = self.bridge.reference
id = self.bridge.documentID
data = self.bridge.data(with: serverTimestamp)
createTime = self.bridge.create_time
updateTime = self.bridge.update_time
}

/// The reference of the document, if the query returns the `__name__` field.
public let ref: DocumentReference?

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

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

/// Retrieves the field specified by `fieldPath`.
/// - Parameter fieldPath: The field path (e.g., "foo" or "foo.bar").
/// - Returns: The data at the specified field location or `nil` if no such field exists.
public func get(_ field: Field) -> Sendable? {
return bridge.get(field.fieldName)
return bridge.get(field.fieldName, serverTimestampBehavior: serverTimestamp)
}
}
61 changes: 28 additions & 33 deletions Firestore/Swift/Source/SwiftAPI/Pipeline/RealtimePipeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,6 @@ import Foundation

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
public struct PipelineListenOptions: Sendable, Equatable, Hashable {
/// Defines how to handle server-generated timestamps that are not yet known locally
/// during latency compensation.
public struct ServerTimestampBehavior: Sendable, Equatable, Hashable {
/// The raw string value for the behavior, used for implementation and hashability.
let rawValue: String

/// Creates a new behavior with a private raw value.
private init(rawValue: String) {
self.rawValue = rawValue
}

/// Fields dependent on server timestamps will be `nil` until the value is
/// confirmed by the server.
public static let none = ServerTimestampBehavior(rawValue: "none")

/// Fields dependent on server timestamps will receive a local, client-generated
/// time estimate until the value is confirmed by the server.
public static let estimate = ServerTimestampBehavior(rawValue: "estimate")

/// Fields dependent on server timestamps will hold the value from the last
/// server-confirmed write until the new value is confirmed.
public static let previous = ServerTimestampBehavior(rawValue: "previous")
}

// MARK: - Stored Properties

/// The desired behavior for handling pending server timestamps.
Expand All @@ -70,16 +46,31 @@ public struct PipelineListenOptions: Sendable, Equatable, Hashable {
self.includeMetadataChanges = includeMetadataChanges
self.source = source
bridge = __PipelineListenOptionsBridge(
serverTimestampBehavior: (self.serverTimestamps ?? .none).rawValue,
serverTimestampBehavior: PipelineListenOptions
.toRawValue(servertimestamp: self.serverTimestamps ?? .none),
includeMetadata: self.includeMetadataChanges ?? false,
source: self.source ?? ListenSource.default
)
}

private static func toRawValue(servertimestamp: ServerTimestampBehavior) -> String {
switch servertimestamp {
case .none:
return "none"
case .estimate:
return "estimate"
case .previous:
return "previous"
@unknown default:
fatalError("Unknown server timestamp behavior")
}
}
}

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
public struct RealtimePipeline: @unchecked Sendable {
private var stages: [Stage]

let bridge: RealtimePipelineBridge
let db: Firestore

Expand All @@ -93,14 +84,18 @@ public struct RealtimePipeline: @unchecked Sendable {
listener: @escaping (RealtimePipelineSnapshot?, Error?) -> Void)
-> ListenerRegistration {
return bridge.addSnapshotListener(options: options.bridge) { snapshotBridge, error in
listener(
RealtimePipelineSnapshot(
// TODO(pipeline): this needs to be fixed
snapshotBridge!,
pipeline: self
),
error
)
if snapshotBridge != nil {
listener(
RealtimePipelineSnapshot(
snapshotBridge!,
pipeline: self,
options: options
),
error
)
} else {
listener(nil, error)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ public struct RealtimePipelineSnapshot: Sendable {
public let metadata: SnapshotMetadata

let bridge: __RealtimePipelineSnapshotBridge
private var options: PipelineListenOptions

init(_ bridge: __RealtimePipelineSnapshotBridge, pipeline: RealtimePipeline) {
init(_ bridge: __RealtimePipelineSnapshotBridge,
pipeline: RealtimePipeline,
options: PipelineListenOptions) {
self.bridge = bridge
self.pipeline = pipeline
self.options = options
metadata = bridge.metadata
results_cache = self.bridge.results.map { PipelineResult($0) }
results_cache = self.bridge.results
.map { PipelineResult($0, options.serverTimestamps ?? .none) }
changes = self.bridge.changes.map { PipelineResultChange($0) }
}

Expand Down
Loading
Loading