Swift: Firestore-compatible API
Firebase-style collection, document and query references on top of Sovaria documents, for moving a Firebase app over with the Swift SDK.
Firebase-style collection, document and query references on top of Sovaria documents, for moving a Firebase app over. Every call runs as the client's signed-in user, so your project's policies and row-level security apply. Types and members are listed in source order; each one links to its HTTP endpoint where it maps to one.
#Types and members
Firestore
The Firebase iOS SDK's Firestore API on Sovaria documents: move an app by changing imports and how db is made.
Names, arguments and shapes follow Firebase (data(), exists, documentChanges, ListenerRegistration, data(as:) / setData(from:) for Codable). Underneath it is the Sovaria collection API, so collection rules and row policies decide every read and write. Differences: timestamps are stored as ISO-8601 strings (Timestamp and Date values you write are converted; read them back with Timestamp(iso:)), the transaction closure is async (reads go to the server), and there is no offline document cache.
public final class Firestore: @unchecked SendableFirestore.sovaria
A property.
public let sovaria: SovariaFirestore.callbackQueue
Where snapshot listeners are called: the main queue on Apple platforms (like Firebase), else the thread that received the change.
public var callbackQueue: DispatchQueue?Set it before adding listeners.
Firestore.firestore
The Firestore handle for a Sovaria client (the same one while you keep it).
public static func firestore(_ sovaria: Sovaria) -> FirestoreFirestore.collection
collection("users") or collection("users/\(uid)/posts").
public func collection(_ collectionPath: String) -> CollectionReferenceFirestore.document
document("users/\(uid)").
public func document(_ documentPath: String) -> DocumentReferenceFirestore.collectionGroup
Every collection with this id at any depth (comments, posts/p1/comments, …).
public func collectionGroup(_ collectionID: String) -> QueryFirestore.batch
public func batch() -> WriteBatchFirestore.runTransaction
Reads then writes atomically; retried (up to maxAttempts) when a document it read changed meanwhile.
public func runTransaction<T>(maxAttempts: Int = 5, _ body: (Transaction) async throws -> T) async throws -> TUnlike Firebase's synchronous block, transaction.getDocument is async.
Timestamp
A point in time. Stored as an ISO-8601 string in Sovaria.
public struct Timestamp: Hashable, Comparable, Codable, SendableTimestamp.seconds
A property.
public let seconds: Int64Timestamp.nanoseconds
A property.
public let nanoseconds: Int32Timestamp.Timestamp.init
Create an instance.
public init(seconds: Int64, nanoseconds: Int32)Timestamp.Timestamp.init
Create an instance.
public init(date: Date)Timestamp.Timestamp.init
Read back a stored timestamp (an ISO string).
public init?(iso: String)Timestamp.now
public static func now() -> TimestampTimestamp.dateValue
public func dateValue() -> DateTimestamp.iso
A property.
public var iso: StringTimestamp.func
public static func < (a: Timestamp, b: Timestamp) -> BoolVectorValue
A vector value (FieldValue.vector([...])); stored as an array of numbers.
public struct VectorValue: Hashable, SendableVectorValue.array
A property.
public let array: [Double]VectorValue.VectorValue.init
Create an instance.
public init(_ array: [Double])FieldPath
A field path: FieldPath(["address", "city"]), or FieldPath.documentID() to filter or order by the id.
public struct FieldPath: Hashable, SendableFieldPath.components
A property.
public let components: [String]FieldPath.FieldPath.init
Create an instance.
public init(_ fieldNames: [String])FieldPath.documentID
public static func documentID() -> FieldPathFieldValue
Atomic field updates, applied by the server inside the commit.
public final class FieldValue: @unchecked SendableFieldValue.delete
public static func delete() -> FieldValueFieldValue.serverTimestamp
public static func serverTimestamp() -> FieldValueFieldValue.arrayUnion
public static func arrayUnion(_ elements: [Any]) -> FieldValueFieldValue.arrayRemove
public static func arrayRemove(_ elements: [Any]) -> FieldValueFieldValue.increment
public static func increment(_ n: Int64) -> FieldValueFieldValue.increment
public static func increment(_ n: Double) -> FieldValueFieldValue.vector
public static func vector(_ values: [Double]) -> VectorValueDocumentReference
public final class DocumentReference: Hashable, @unchecked SendableDocumentReference.firestore
A property.
public let firestore: FirestoreDocumentReference.path
A property.
public let path: StringDocumentReference.documentID
A property.
public var documentID: StringDocumentReference.parent
A property.
public var parent: CollectionReferenceDocumentReference.collection
public func collection(_ collectionPath: String) -> CollectionReferenceDocumentReference.getDocument
public func getDocument() async throws -> DocumentSnapshotDocumentReference.setData
Replace the document, or merge into it with merge: true.
public func setData(_ documentData: [String: Any], merge: Bool = false) async throwsDocumentReference.setData
Merge only these fields (String or FieldPath).
public func setData(_ documentData: [String: Any], mergeFields: [Any]) async throwsDocumentReference.setData
Codable: try await ref.setData(from: user).
public func setData<T: Encodable>(from value: T, merge: Bool = false) async throwsDocumentReference.updateData
Change fields ("a.b" dotted paths and FieldPath keys allowed); fails with 404 when it doesn't exist.
public func updateData(_ fields: [AnyHashable: Any]) async throwsDocumentReference.delete
public func delete() async throwsDocumentReference.addSnapshotListener
The document now and after every change.
public func addSnapshotListener(_ listener: @escaping (DocumentSnapshot?, Error?) -> Void) -> ListenerRegistrationKeep the registration; remove() stops it.
DocumentReference.func
public static func == (a: DocumentReference, b: DocumentReference) -> BoolDocumentReference.hash
public func hash(into h: inout Hasher)Filter
One filter or an or / and group: whereFilter(Filter.orFilter([Filter.whereField("a", isEqualTo: 1), …])).
public struct Filter: @unchecked SendableFilter.whereField
public static func whereField(_ field: Any, isEqualTo value: Any?) -> FilterFilter.whereField
public static func whereField(_ field: Any, isNotEqualTo value: Any?) -> FilterFilter.whereField
public static func whereField(_ field: Any, isLessThan value: Any) -> FilterFilter.whereField
public static func whereField(_ field: Any, isLessThanOrEqualTo value: Any) -> FilterFilter.whereField
public static func whereField(_ field: Any, isGreaterThan value: Any) -> FilterFilter.whereField
public static func whereField(_ field: Any, isGreaterThanOrEqualTo value: Any) -> FilterFilter.whereField
public static func whereField(_ field: Any, arrayContains value: Any) -> FilterFilter.whereField
public static func whereField(_ field: Any, arrayContainsAny values: [Any]) -> FilterFilter.whereField
public static func whereField(_ field: Any, in values: [Any]) -> FilterFilter.whereField
public static func whereField(_ field: Any, notIn values: [Any]) -> FilterFilter.orFilter
Documents matching any of the filters (at most 30 OR terms after expansion).
public static func orFilter(_ filters: [Filter]) -> FilterFilter.andFilter
Documents matching all of the filters, for nesting inside orFilter.
public static func andFilter(_ filters: [Filter]) -> FilterQuery
public class Query: @unchecked SendableQuery.firestore
A property.
public let firestore: FirestoreQuery.parameters
The request body (for tests and debugging).
public var parameters: [String: Any]Query.whereFilter
public func whereFilter(_ filter: Filter) -> QueryQuery.whereField
public func whereField(_ field: Any, isEqualTo value: Any?) -> QueryQuery.whereField
public func whereField(_ field: Any, isNotEqualTo value: Any?) -> QueryQuery.whereField
public func whereField(_ field: Any, isLessThan value: Any) -> QueryQuery.whereField
public func whereField(_ field: Any, isLessThanOrEqualTo value: Any) -> QueryQuery.whereField
public func whereField(_ field: Any, isGreaterThan value: Any) -> QueryQuery.whereField
public func whereField(_ field: Any, isGreaterThanOrEqualTo value: Any) -> QueryQuery.whereField
public func whereField(_ field: Any, arrayContains value: Any) -> QueryQuery.whereField
public func whereField(_ field: Any, arrayContainsAny values: [Any]) -> QueryQuery.whereField
public func whereField(_ field: Any, in values: [Any]) -> QueryQuery.whereField
public func whereField(_ field: Any, notIn values: [Any]) -> QueryQuery.order
public func order(by field: Any, descending: Bool = false) -> QueryQuery.limit
public func limit(to n: Int) -> QueryQuery.limit
public func limit(toLast n: Int) -> QueryQuery.start
public func start(at fieldValues: [Any]) -> QueryQuery.start
public func start(after fieldValues: [Any]) -> QueryQuery.end
public func end(at fieldValues: [Any]) -> QueryQuery.end
public func end(before fieldValues: [Any]) -> QueryQuery.start
public func start(atDocument d: DocumentSnapshot) -> QueryQuery.start
public func start(afterDocument d: DocumentSnapshot) -> QueryQuery.end
public func end(atDocument d: DocumentSnapshot) -> QueryQuery.end
public func end(beforeDocument d: DocumentSnapshot) -> QueryQuery.getDocuments
public func getDocuments() async throws -> QuerySnapshotQuery.addSnapshotListener
Live results: the listener runs with the first snapshot and after every change.
public func addSnapshotListener(_ listener: @escaping (QuerySnapshot?, Error?) -> Void) -> ListenerRegistrationremove() stops it.
Query.count
query.count.getAggregation(source: .server).count.
public var count: AggregateQueryQuery.aggregate
aggregate([.count(), .sum("price"), .average("rating")]).getAggregation(source: .server).
public func aggregate(_ fields: [AggregateField]) -> AggregateQueryQuery.findNearest
Vector search: this query's filters first, then the limit documents whose vectorField is closest.
public func findNearest(vectorField: Any, queryVector: VectorValue, limit: Int, distanceMeasure: DistanceMeasure,
distanceResultField: String? = nil, distanceThreshold: Double? = nil) -> VectorQueryQuery.findNearest
public func findNearest(vectorField: Any, queryVector: [Double], limit: Int, distanceMeasure: DistanceMeasure,
distanceResultField: String? = nil, distanceThreshold: Double? = nil) -> VectorQueryCollectionReference
public final class CollectionReference: Query, @unchecked SendableCollectionReference.collectionID
A property.
public var collectionID: StringCollectionReference.collectionPath
A property.
public var collectionPath: StringCollectionReference.parent
The document this subcollection belongs to, or nil for a top-level collection.
public var parent: DocumentReference?CollectionReference.document
A new document with a random id.
public func document() -> DocumentReferenceCollectionReference.document
public func document(_ documentPath: String) -> DocumentReferenceCollectionReference.addDocument
Create a document with a new id.
public func addDocument(data: [String: Any]) async throws -> DocumentReferenceCollectionReference.addDocument
public func addDocument<T: Encodable>(from value: T) async throws -> DocumentReferenceDistanceMeasure
public enum DistanceMeasure: String, SendableVectorQuery
public final class VectorQuery: @unchecked SendableVectorQuery.getDocuments
public func getDocuments() async throws -> QuerySnapshotAggregateField
public struct AggregateField: Hashable, SendableAggregateField.count
public static func count() -> AggregateFieldAggregateField.sum
public static func sum(_ field: String) -> AggregateFieldAggregateField.sum
public static func sum(_ field: FieldPath) -> AggregateFieldAggregateField.average
public static func average(_ field: String) -> AggregateFieldAggregateField.average
public static func average(_ field: FieldPath) -> AggregateFieldAggregateSource
public enum AggregateSource: SendableAggregateQuery
public final class AggregateQuery: @unchecked SendableAggregateQuery.query
A property.
public let query: QueryAggregateQuery.getAggregation
public func getAggregation(source: AggregateSource = .server) async throws -> AggregateQuerySnapshotAggregateQuerySnapshot
public final class AggregateQuerySnapshot: @unchecked SendableAggregateQuerySnapshot.count
A property.
public var count: NSNumberAggregateQuerySnapshot.get
The value for field (an NSNumber, or nil when there was nothing to average).
public func get(_ field: AggregateField) -> Any?SnapshotMetadata
public struct SnapshotMetadata: Hashable, SendableSnapshotMetadata.hasPendingWrites
A property.
public let hasPendingWrites: BoolSnapshotMetadata.isFromCache
A property.
public let isFromCache: BoolDocumentSnapshot
public class DocumentSnapshot: @unchecked SendableDocumentSnapshot.reference
A property.
public let reference: DocumentReferenceDocumentSnapshot.documentID
A property.
public var documentID: StringDocumentSnapshot.exists
A property.
public var exists: BoolDocumentSnapshot.metadata
A property.
public var metadata: SnapshotMetadataDocumentSnapshot.data
The data, or nil when the document doesn't exist.
public func data() -> [String: Any]?DocumentSnapshot.get
A field by dotted path (get("address.city")) or FieldPath; nil when missing.
public func get(_ field: Any) -> Any?DocumentSnapshot.subscript
A property.
public subscript(field: Any) -> Any?DocumentSnapshot.data
Codable: try snap.data(as: User.self).
public func data<T: Decodable>(as type: T.Type) throws -> TQueryDocumentSnapshot
public final class QueryDocumentSnapshot: DocumentSnapshot, @unchecked SendableQueryDocumentSnapshot.data
public override func data() -> [String: Any]DocumentChangeType
public enum DocumentChangeType: SendableDocumentChange
public struct DocumentChange: @unchecked SendableDocumentChange.type
A property.
public let type: DocumentChangeTypeDocumentChange.document
A property.
public let document: QueryDocumentSnapshotDocumentChange.oldIndex
UInt(NSNotFound) for an added document, like Firebase.
public let oldIndex: UIntDocumentChange.newIndex
UInt(NSNotFound) for a removed document.
public let newIndex: UIntQuerySnapshot
public final class QuerySnapshot: @unchecked SendableQuerySnapshot.query
A property.
public let query: QueryQuerySnapshot.documents
A property.
public let documents: [QueryDocumentSnapshot]QuerySnapshot.documentChanges
A property.
public let documentChanges: [DocumentChange]QuerySnapshot.count
A property.
public var count: IntQuerySnapshot.isEmpty
A property.
public var isEmpty: BoolQuerySnapshot.metadata
A property.
public var metadata: SnapshotMetadataListenerRegistration
public protocol ListenerRegistration: AnyObjectListenerRegistration.remove
func remove()WriteBatch
Writes committed together: every one or none.
public final class WriteBatch: @unchecked SendableWriteBatch.setData
public func setData(_ data: [String: Any], forDocument document: DocumentReference, merge: Bool = false) -> WriteBatchWriteBatch.setData
public func setData(_ data: [String: Any], forDocument document: DocumentReference, mergeFields: [Any]) -> WriteBatchWriteBatch.updateData
public func updateData(_ fields: [AnyHashable: Any], forDocument document: DocumentReference) -> WriteBatchWriteBatch.deleteDocument
public func deleteDocument(_ document: DocumentReference) -> WriteBatchWriteBatch.commit
public func commit() async throwsTransaction
Read every document first, then write; the commit fails (and is retried) if a read document changed.
public final class Transaction: @unchecked SendableTransaction.getDocument
public func getDocument(_ document: DocumentReference) async throws -> DocumentSnapshotTransaction.setData
public func setData(_ data: [String: Any], forDocument document: DocumentReference, merge: Bool = false) -> TransactionTransaction.updateData
public func updateData(_ fields: [AnyHashable: Any], forDocument document: DocumentReference) -> TransactionTransaction.deleteDocument
public func deleteDocument(_ document: DocumentReference) -> Transaction