| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- // These aren't tests in the usual sense--they just verify that the Objective-C to Swift translation
- // results in the right names.
- import Foundation
- import XCTest
- import FirebaseFirestore
- class BasicCompileTests: XCTestCase {
- func testCompiled() {
- XCTAssertTrue(true)
- }
- }
- func main() {
- let db = initializeDb()
- let (collectionRef, documentRef) = makeRefs(database: db)
- let query = makeQuery(collection: collectionRef)
- writeDocument(at: documentRef)
- writeDocuments(at: documentRef, database: db)
- addDocument(to: collectionRef)
- readDocument(at: documentRef)
- readDocumentWithSource(at: documentRef)
- readDocuments(matching: query)
- readDocumentsWithSource(matching: query)
- listenToDocument(at: documentRef)
- listenToDocuments(matching: query)
- enableDisableNetwork(database: db)
- clearPersistence(database: db)
- types()
- waitForPendingWrites(database: db)
- addSnapshotsInSyncListener(database: db)
- terminateDb(database: db)
- }
- func initializeDb() -> Firestore {
- // Initialize with ProjectID.
- let firestore = Firestore.firestore()
- // Apply settings
- let settings = FirestoreSettings()
- settings.host = "localhost"
- settings.isPersistenceEnabled = true
- settings.cacheSizeBytes = FirestoreCacheSizeUnlimited
- firestore.settings = settings
- return firestore
- }
- func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference) {
- var collectionRef = db.collection("my-collection")
- var documentRef: DocumentReference
- documentRef = collectionRef.document("my-doc")
- // or
- documentRef = db.document("my-collection/my-doc")
- // deeper collection (my-collection/my-doc/some/deep/collection)
- collectionRef = documentRef.collection("some/deep/collection")
- // parent doc (my-collection/my-doc/some/deep)
- documentRef = collectionRef.parent!
- // print paths.
- print("Collection: \(collectionRef.path), document: \(documentRef.path)")
- return (collectionRef, documentRef)
- }
- func makeQuery(collection collectionRef: CollectionReference) -> Query {
- var query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred")
- .whereField("age", isGreaterThanOrEqualTo: 24)
- .whereField("tags", arrayContains: "active")
- .whereField(FieldPath(["tags"]), arrayContains: "active")
- .whereField("tags", arrayContainsAny: ["active", "squat"])
- .whereField(FieldPath(["tags"]), arrayContainsAny: ["active", "squat"])
- .whereField("tags", in: ["active", "squat"])
- .whereField(FieldPath(["tags"]), in: ["active", "squat"])
- .whereField("tags", notIn: ["active", "squat"])
- .whereField(FieldPath(["tags"]), notIn: ["active", "squat"])
- .whereField(FieldPath.documentID(), isEqualTo: "fred")
- .whereField(FieldPath.documentID(), isNotEqualTo: "fred")
- .whereField(FieldPath(["name"]), isEqualTo: "fred")
- .order(by: FieldPath(["age"]))
- .order(by: "name", descending: true)
- .limit(to: 10)
- .limit(toLast: 10)
- query = collectionRef.firestore.collectionGroup("collection")
- return query
- }
- func writeDocument(at docRef: DocumentReference) {
- let setData = [
- "foo": 42,
- "bar": [
- "baz": "Hello world!",
- ],
- ] as [String: Any]
- let updateData = [
- "bar.baz": 42,
- FieldPath(["foobar"]): 42,
- "server_timestamp": FieldValue.serverTimestamp(),
- "array_union": FieldValue.arrayUnion(["a", "b"]),
- "array_remove": FieldValue.arrayRemove(["a", "b"]),
- "field_delete": FieldValue.delete(),
- ] as [AnyHashable: Any]
- docRef.setData(setData)
- // Completion callback (via trailing closure syntax).
- docRef.setData(setData) { error in
- if let error = error {
- print("Uh oh! \(error)")
- return
- }
- print("Set complete!")
- }
- // merge
- docRef.setData(setData, merge: true)
- docRef.setData(setData, merge: true) { error in
- if let error = error {
- print("Uh oh! \(error)")
- return
- }
- print("Set complete!")
- }
- docRef.updateData(updateData)
- docRef.delete()
- docRef.delete { error in
- if let error = error {
- print("Uh oh! \(error)")
- return
- }
- print("Set complete!")
- }
- }
- func enableDisableNetwork(database db: Firestore) {
- // closure syntax
- db.disableNetwork(completion: { error in
- if let e = error {
- print("Uh oh! \(e)")
- return
- }
- })
- // trailing block syntax
- db.enableNetwork { error in
- if let e = error {
- print("Uh oh! \(e)")
- return
- }
- }
- }
- func clearPersistence(database db: Firestore) {
- db.clearPersistence { error in
- if let e = error {
- print("Uh oh! \(e)")
- return
- }
- }
- }
- func writeDocuments(at docRef: DocumentReference, database db: Firestore) {
- var batch: WriteBatch
- batch = db.batch()
- batch.setData(["a": "b"], forDocument: docRef)
- batch.setData(["a": "b"], forDocument: docRef, merge: true)
- batch.setData(["c": "d"], forDocument: docRef)
- // commit without completion callback.
- batch.commit()
- print("Batch write without completion complete!")
- batch = db.batch()
- batch.setData(["a": "b"], forDocument: docRef)
- batch.setData(["c": "d"], forDocument: docRef)
- // commit with completion callback via trailing closure syntax.
- batch.commit { error in
- if let error = error {
- print("Uh oh! \(error)")
- return
- }
- print("Batch write callback complete!")
- }
- print("Batch write with completion complete!")
- }
- func addDocument(to collectionRef: CollectionReference) {
- _ = collectionRef.addDocument(data: ["foo": 42])
- // or
- collectionRef.document().setData(["foo": 42])
- }
- func readDocument(at docRef: DocumentReference) {
- // Trailing closure syntax.
- docRef.getDocument { document, error in
- if let document = document {
- // Note that both document and document.data() is nullable.
- if let data = document.data() {
- print("Read document: \(data)")
- }
- if let data = document.data(with: .estimate) {
- print("Read document: \(data)")
- }
- if let foo = document.get("foo") {
- print("Field: \(foo)")
- }
- if let foo = document.get("foo", serverTimestampBehavior: .previous) {
- print("Field: \(foo)")
- }
- // Fields can also be read via subscript notation.
- if let foo = document["foo"] {
- print("Field: \(foo)")
- }
- } else if let error = error {
- // New way to handle errors.
- switch error {
- case FirestoreErrorCode.unavailable:
- print("Can't read document due to being offline!")
- default:
- print("Failed to read.")
- }
- // Old way to handle errors.
- let nsError = error as NSError
- guard nsError.domain == FirestoreErrorDomain else {
- print("Unknown error!")
- return
- }
- // Option 1: try to initialize the error code enum.
- if let errorCode = FirestoreErrorCode.Code(rawValue: nsError.code) {
- switch errorCode {
- case .unavailable:
- print("Can't read document due to being offline!")
- default:
- print("Failed to read.")
- }
- }
- // Option 2: switch on the code and compare agianst raw values.
- switch nsError.code {
- case FirestoreErrorCode.unavailable.rawValue:
- print("Can't read document due to being offline!")
- default:
- print("Failed to read.")
- }
- } else {
- print("No error or document. Thanks, ObjC.")
- }
- }
- }
- func readDocumentWithSource(at docRef: DocumentReference) {
- docRef.getDocument(source: FirestoreSource.default) { _, _ in
- }
- docRef.getDocument(source: .server) { _, _ in
- }
- docRef.getDocument(source: FirestoreSource.cache) { _, _ in
- }
- }
- func readDocuments(matching query: Query) {
- query.getDocuments { querySnapshot, _ in
- // TODO(mikelehen): Figure out how to make "for..in" syntax work
- // directly on documentSet.
- for document in querySnapshot!.documents {
- print(document.data())
- }
- }
- }
- func readDocumentsWithSource(matching query: Query) {
- query.getDocuments(source: FirestoreSource.default) { _, _ in
- }
- query.getDocuments(source: .server) { _, _ in
- }
- query.getDocuments(source: FirestoreSource.cache) { _, _ in
- }
- }
- func listenToDocument(at docRef: DocumentReference) {
- let listener = docRef.addSnapshotListener { document, error in
- if let error = error {
- print("Uh oh! Listen canceled: \(error)")
- return
- }
- if let document = document {
- // Note that document.data() is nullable.
- if let data: [String: Any] = document.data() {
- print("Current document: \(data)")
- }
- if document.metadata.isFromCache {
- print("From Cache")
- } else {
- print("From Server")
- }
- }
- }
- // Unsubscribe.
- listener.remove()
- }
- func listenToDocumentWithMetadataChanges(at docRef: DocumentReference) {
- let listener = docRef.addSnapshotListener(includeMetadataChanges: true) { document, _ in
- if let document = document {
- if document.metadata.hasPendingWrites {
- print("Has pending writes")
- }
- }
- }
- // Unsubscribe.
- listener.remove()
- }
- func listenToDocuments(matching query: Query) {
- let listener = query.addSnapshotListener { snap, error in
- if let error = error {
- print("Uh oh! Listen canceled: \(error)")
- return
- }
- if let snap = snap {
- print("NEW SNAPSHOT (empty=\(snap.isEmpty) count=\(snap.count)")
- // TODO(mikelehen): Figure out how to make "for..in" syntax work
- // directly on documentSet.
- for document in snap.documents {
- // Note that document.data() is not nullable.
- let data: [String: Any] = document.data()
- print("Doc: ", data)
- }
- }
- }
- // Unsubscribe
- listener.remove()
- }
- func listenToQueryDiffs(onQuery query: Query) {
- let listener = query.addSnapshotListener { snap, _ in
- if let snap = snap {
- for change in snap.documentChanges {
- switch change.type {
- case .added:
- print("New document: \(change.document.data())")
- case .modified:
- print("Modified document: \(change.document.data())")
- case .removed:
- print("Removed document: \(change.document.data())")
- }
- }
- }
- }
- // Unsubscribe
- listener.remove()
- }
- func listenToQueryDiffsWithMetadata(onQuery query: Query) {
- let listener = query.addSnapshotListener(includeMetadataChanges: true) { snap, _ in
- if let snap = snap {
- for change in snap.documentChanges(includeMetadataChanges: true) {
- switch change.type {
- case .added:
- print("New document: \(change.document.data())")
- case .modified:
- print("Modified document: \(change.document.data())")
- case .removed:
- print("Removed document: \(change.document.data())")
- }
- }
- }
- }
- // Unsubscribe
- listener.remove()
- }
- func transactions() {
- let db = Firestore.firestore()
- let collectionRef = db.collection("cities")
- let accA = collectionRef.document("accountA")
- let accB = collectionRef.document("accountB")
- let amount = 20.0
- db.runTransaction({ transaction, errorPointer -> Any? in
- do {
- let balanceA = try transaction.getDocument(accA)["balance"] as! Double
- let balanceB = try transaction.getDocument(accB)["balance"] as! Double
- if balanceA < amount {
- errorPointer?.pointee = NSError(domain: "Foo", code: 123, userInfo: nil)
- return nil
- }
- transaction.updateData(["balance": balanceA - amount], forDocument: accA)
- transaction.updateData(["balance": balanceB + amount], forDocument: accB)
- } catch let error as NSError {
- print("Uh oh! \(error)")
- }
- return 0
- }) { _, _ in
- // handle result.
- }
- }
- func types() {
- let _: CollectionReference
- let _: DocumentChange
- let _: DocumentReference
- let _: DocumentSnapshot
- let _: FieldPath
- let _: FieldValue
- let _: Firestore
- let _: FirestoreSettings
- let _: GeoPoint
- let _: Timestamp
- let _: ListenerRegistration
- let _: Query
- let _: QuerySnapshot
- let _: SnapshotMetadata
- let _: Transaction
- let _: WriteBatch
- }
- func waitForPendingWrites(database db: Firestore) {
- db.waitForPendingWrites { error in
- if let e = error {
- print("Uh oh! \(e)")
- return
- }
- }
- }
- func addSnapshotsInSyncListener(database db: Firestore) {
- let listener = db.addSnapshotsInSyncListener {}
- // Unsubscribe
- listener.remove()
- }
- func terminateDb(database db: Firestore) {
- db.terminate { error in
- if let e = error {
- print("Uh oh! \(e)")
- return
- }
- }
- }
- #if swift(>=5.5.2)
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- func testAsyncAwait(database db: Firestore) async throws {
- try await db.enableNetwork()
- try await db.disableNetwork()
- try await db.waitForPendingWrites()
- try await db.clearPersistence()
- try await db.terminate()
- try await db.runTransaction { _, _ in
- 0
- }
- let batch = db.batch()
- try await batch.commit()
- _ = await db.getQuery(named: "foo")
- _ = try await db.loadBundle(Data())
- let collection = db.collection("coll")
- try await collection.getDocuments()
- try await collection.getDocuments(source: FirestoreSource.default)
- let document = try await collection.addDocument(data: [:])
- try await document.setData([:])
- try await document.setData([:], merge: true)
- try await document.setData([:], mergeFields: [])
- try await document.updateData([:])
- try await document.delete()
- try await document.getDocument()
- try await document.getDocument(source: FirestoreSource.default)
- }
- #endif
|