Skip to content

Commit 3c84e74

Browse files
committed
Add AsyncSequence support for Firestore
This change introduces support for `AsyncSequence` to `DocumentReference` and `Query`, allowing developers to use `for await in` to receive real-time updates from Firestore. The implementation wraps the existing snapshot listener APIs in an `AsyncThrowingStream`, providing a modern, Swift-native way to work with real-time data. This change also includes unit tests for the new functionality.
1 parent 4f7dd49 commit 3c84e74

File tree

3 files changed

+25
-28
lines changed

3 files changed

+25
-28
lines changed

Firestore/Swift/Source/AsyncAwait/DocumentReference+AsyncSequence.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ public extension DocumentReference {
3636
/// - Returns: An `AsyncThrowingStream` of `DocumentSnapshot` events.
3737
func snapshots(includeMetadataChanges: Bool) -> AsyncThrowingStream<DocumentSnapshot, Error> {
3838
return AsyncThrowingStream { continuation in
39-
let listener = self.addSnapshotListener(includeMetadataChanges: includeMetadataChanges) { snapshot, error in
40-
if let error = error {
41-
continuation.finish(throwing: error)
42-
} else if let snapshot = snapshot {
43-
continuation.yield(snapshot)
39+
let listener = self
40+
.addSnapshotListener(includeMetadataChanges: includeMetadataChanges) { snapshot, error in
41+
if let error = error {
42+
continuation.finish(throwing: error)
43+
} else if let snapshot = snapshot {
44+
continuation.yield(snapshot)
45+
}
4446
}
45-
}
4647
continuation.onTermination = { @Sendable _ in
4748
listener.remove()
4849
}

Firestore/Swift/Source/AsyncAwait/Query+AsyncSequence.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ public extension Query {
3636
/// - Returns: An `AsyncThrowingStream` of `QuerySnapshot` events.
3737
func snapshots(includeMetadataChanges: Bool) -> AsyncThrowingStream<QuerySnapshot, Error> {
3838
return AsyncThrowingStream { continuation in
39-
let listener = self.addSnapshotListener(includeMetadataChanges: includeMetadataChanges) { snapshot, error in
40-
if let error = error {
41-
continuation.finish(throwing: error)
42-
} else if let snapshot = snapshot {
43-
continuation.yield(snapshot)
39+
let listener = self
40+
.addSnapshotListener(includeMetadataChanges: includeMetadataChanges) { snapshot, error in
41+
if let error = error {
42+
continuation.finish(throwing: error)
43+
} else if let snapshot = snapshot {
44+
continuation.yield(snapshot)
45+
}
4446
}
45-
}
4647
continuation.onTermination = { @Sendable _ in
4748
listener.remove()
4849
}

Firestore/Swift/Tests/Unit/AsyncAwait/AsyncSequenceTests.swift

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
import XCTest
1817
@testable import FirebaseFirestore
18+
import XCTest
1919

2020
// MARK: - Mock Objects for Testing
2121

@@ -33,17 +33,14 @@ private class MockQuery: Query {
3333
var capturedListener: SnapshotListener?
3434
let mockListenerRegistration = MockListenerRegistration()
3535

36-
override func addSnapshotListener(
37-
_ listener: @escaping SnapshotListener
38-
) -> ListenerRegistration {
36+
override func addSnapshotListener(_ listener: @escaping SnapshotListener)
37+
-> ListenerRegistration {
3938
capturedListener = listener
4039
return mockListenerRegistration
4140
}
4241

43-
override func addSnapshotListener(
44-
includeMetadataChanges: Bool,
45-
listener: @escaping SnapshotListener
46-
) -> ListenerRegistration {
42+
override func addSnapshotListener(includeMetadataChanges: Bool,
43+
listener: @escaping SnapshotListener) -> ListenerRegistration {
4744
capturedListener = listener
4845
return mockListenerRegistration
4946
}
@@ -53,17 +50,15 @@ private class MockDocumentReference: DocumentReference {
5350
var capturedListener: DocumentSnapshotListener?
5451
let mockListenerRegistration = MockListenerRegistration()
5552

56-
override func addSnapshotListener(
57-
_ listener: @escaping DocumentSnapshotListener
58-
) -> ListenerRegistration {
53+
override func addSnapshotListener(_ listener: @escaping DocumentSnapshotListener)
54+
-> ListenerRegistration {
5955
capturedListener = listener
6056
return mockListenerRegistration
6157
}
6258

63-
override func addSnapshotListener(
64-
includeMetadataChanges: Bool,
65-
listener: @escaping DocumentSnapshotListener
66-
) -> ListenerRegistration {
59+
override func addSnapshotListener(includeMetadataChanges: Bool,
60+
listener: @escaping DocumentSnapshotListener)
61+
-> ListenerRegistration {
6762
capturedListener = listener
6863
return mockListenerRegistration
6964
}
@@ -180,4 +175,4 @@ class AsyncSequenceTests: XCTestCase {
180175

181176
XCTAssertTrue(mockDocRef.mockListenerRegistration.isRemoved)
182177
}
183-
}
178+
}

0 commit comments

Comments
 (0)