Skip to content

Commit

Permalink
Make CollectionReference implement Query (#32)
Browse files Browse the repository at this point in the history
Given that C++ types are exposed as structs and structs do not support inheritance in Swift, the fact that the C++ `CollectionReference` inherits from `Query` is not exposed on the Swift side. We then workaround that by defining a protocol that both `Query` and `CollectionReference` can conform to.

Also expose `CollectionReference.path()` and `FieldValue.serverTimestamp()`.
  • Loading branch information
darinf authored Feb 16, 2024
1 parent d40d17b commit a2bf484
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 12 deletions.
10 changes: 10 additions & 0 deletions Sources/FirebaseFirestore/CollectionReference+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,14 @@ extension CollectionReference {
public func document(_ path: String) -> DocumentReference {
swift_firebase.swift_cxx_shims.firebase.firestore.collection_document(self, std.string(path))
}

public var path: String {
String(swift_firebase.swift_cxx_shims.firebase.firestore.collection_path(self))
}
}

extension CollectionReference: QueryProtocol {
public var _asQuery: Query {
swift_firebase.swift_cxx_shims.firebase.firestore.collection_as_query(self)
}
}
1 change: 0 additions & 1 deletion Sources/FirebaseFirestore/DocumentSnapshot+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ private typealias MapFieldValue = firebase.firestore.MapFieldValue
public typealias DocumentSnapshot = firebase.firestore.DocumentSnapshot
public typealias ServerTimestampBehavior = firebase.firestore.DocumentSnapshot.ServerTimestampBehavior
public typealias GeoPoint = firebase.firestore.GeoPoint
public typealias FieldValue = firebase.firestore.FieldValue

extension DocumentSnapshot {
public var reference: DocumentReference {
Expand Down
12 changes: 12 additions & 0 deletions Sources/FirebaseFirestore/FieldValue+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase

public typealias FieldValue = firebase.firestore.FieldValue

extension FieldValue {
public static func serverTimestamp() {
ServerTimestamp()
}
}
35 changes: 24 additions & 11 deletions Sources/FirebaseFirestore/Query+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,27 @@ import Foundation

public typealias Query = firebase.firestore.Query

extension Query {
// Any types that extend from Query can conform to QueryProtocol to provide the
// functionality of Query. This is needed since structs in Swift do not support
// inheritance and C++ classes are exposed as structs to Swift.
public protocol QueryProtocol {
// This is an internal means to expose `Query` when the conforming type is
// intended to be a subclass of `Query`.
var _asQuery: Query { get }
}

extension Query: QueryProtocol {
public var _asQuery: Query { self }
}

extension QueryProtocol {
public var firestore: Firestore {
swift_firebase.swift_cxx_shims.firebase.firestore.query_firestore(self)
swift_firebase.swift_cxx_shims.firebase.firestore.query_firestore(_asQuery)
}

// This variant is provided for compatibility with the ObjC API.
public func getDocuments(source: FirestoreSource = .default, completion: @escaping (QuerySnapshot?, Error?) -> Void) {
let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(self, source)
let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(_asQuery, source)
future.setCompletion({
let (snapshot, error) = future.resultAndError
DispatchQueue.main.async {
Expand All @@ -28,7 +41,7 @@ extension Query {

public func getDocuments(source: FirestoreSource = .default) async throws -> QuerySnapshot {
try await withCheckedThrowingContinuation { continuation in
let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(self, source)
let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(_asQuery, source)
future.setCompletion({
let (snapshot, error) = future.resultAndError
if let error {
Expand All @@ -48,7 +61,7 @@ extension Query {
typealias ListenerCallback = (QuerySnapshot?, Error?) -> Void
let boxed = Unmanaged.passRetained(listener as AnyObject)
let instance = swift_firebase.swift_cxx_shims.firebase.firestore.query_add_snapshot_listener(
self, { snapshot, errorCode, errorMessage, pvListener in
_asQuery, { snapshot, errorCode, errorMessage, pvListener in
let callback = Unmanaged<AnyObject>.fromOpaque(pvListener!).takeUnretainedValue() as! ListenerCallback

let error = NSError.firestore(errorCode, errorMessage: errorMessage)
Expand Down Expand Up @@ -79,37 +92,37 @@ extension Query {

public func whereField(_ field: String, isEqualTo value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_equal_to(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

public func whereField(_ field: String, isNotEqualTo value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_not_equal_to(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

public func whereField(_ field: String, isLessThan value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_less_than(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

public func whereField(_ field: String, isLessThanOrEqualTo value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_less_than_or_equal_to(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

public func whereField(_ field: String, isGreaterThan value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_greater_than(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

public func whereField(_ field: String, isGreaterThanOrEqualTo value: Any) -> Query {
swift_firebase.swift_cxx_shims.firebase.firestore.query_where_greater_than_or_equal_to(
self, std.string(field), firestoreValueOrFail(value)
_asQuery, std.string(field), firestoreValueOrFail(value)
)
}

Expand Down
12 changes: 12 additions & 0 deletions Sources/firebase/include/FirebaseFirestore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,24 @@ document_snapshot_id(::firebase::firestore::DocumentSnapshot snapshot) {
return snapshot.id();
}

// MARK: CollectionReference

inline ::firebase::firestore::DocumentReference
collection_document(::firebase::firestore::CollectionReference collection,
const ::std::string &document_path) {
return collection.Document(document_path);
}

inline ::std::string
collection_path(const ::firebase::firestore::CollectionReference& collection) {
return collection.path();
}

inline ::firebase::firestore::Query
collection_as_query(::firebase::firestore::CollectionReference collection) {
return collection;
}

typedef void (*DocumentSnapshotListenerTypedCallback)(
const ::firebase::firestore::DocumentSnapshot *snapshot,
::firebase::firestore::Error error_code, const char *error_message,
Expand Down

0 comments on commit a2bf484

Please sign in to comment.