2017-10-03 08:55:22 -07:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
2017-11-21 17:02:22 -08:00
|
|
|
import FirebaseFirestore
|
2017-10-03 08:55:22 -07:00
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
let db = initializeDb();
|
|
|
|
|
|
|
|
|
|
let (collectionRef, documentRef) = makeRefs(database: db);
|
|
|
|
|
|
|
|
|
|
let query = makeQuery(collection: collectionRef);
|
|
|
|
|
|
|
|
|
|
writeDocument(at: documentRef);
|
|
|
|
|
|
2017-12-11 15:37:01 -05:00
|
|
|
writeDocuments(at: documentRef, database: db);
|
|
|
|
|
|
2017-10-03 08:55:22 -07:00
|
|
|
addDocument(to: collectionRef);
|
|
|
|
|
|
|
|
|
|
readDocument(at: documentRef);
|
|
|
|
|
|
|
|
|
|
readDocuments(matching: query);
|
|
|
|
|
|
|
|
|
|
listenToDocument(at: documentRef);
|
|
|
|
|
|
|
|
|
|
listenToDocuments(matching: query);
|
|
|
|
|
|
2017-12-18 21:05:07 -08:00
|
|
|
enableDisableNetwork(database: db);
|
2017-12-18 18:50:08 -08:00
|
|
|
|
2017-10-03 08:55:22 -07:00
|
|
|
types();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func initializeDb() -> Firestore {
|
|
|
|
|
|
|
|
|
|
// Initialize with ProjectID.
|
|
|
|
|
let firestore = Firestore.firestore()
|
|
|
|
|
|
|
|
|
|
// Apply settings
|
|
|
|
|
let settings = FirestoreSettings()
|
|
|
|
|
settings.host = "localhost"
|
|
|
|
|
settings.isPersistenceEnabled = true
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
|
|
let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred")
|
|
|
|
|
.whereField("age", isGreaterThanOrEqualTo: 24)
|
|
|
|
|
.whereField(FieldPath.documentID(), isEqualTo: "fred")
|
|
|
|
|
.order(by: FieldPath(["age"]))
|
|
|
|
|
.order(by: "name", descending: true)
|
|
|
|
|
.limit(to: 10)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
] 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!")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetOptions
|
|
|
|
|
docRef.setData(setData, options:SetOptions.merge())
|
|
|
|
|
|
|
|
|
|
docRef.updateData(updateData)
|
|
|
|
|
docRef.delete();
|
|
|
|
|
|
|
|
|
|
docRef.delete() { error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
print("Uh oh! \(error)")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print("Set complete!")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 21:05:07 -08:00
|
|
|
func enableDisableNetwork(database db: Firestore) {
|
2017-12-18 18:50:08 -08:00
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-11 15:37:01 -05:00
|
|
|
func writeDocuments(at docRef: DocumentReference, database db: Firestore) {
|
|
|
|
|
var batch: WriteBatch;
|
|
|
|
|
|
|
|
|
|
batch = db.batch();
|
|
|
|
|
batch.setData(["a" : "b"], forDocument:docRef);
|
|
|
|
|
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!");
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-03 08:55:22 -07:00
|
|
|
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 {
|
2017-12-14 11:03:41 +08:00
|
|
|
// Note that both document and document.data() is nullable.
|
|
|
|
|
if let data = document.data() {
|
2017-10-03 08:55:22 -07:00
|
|
|
print("Read document: \(data)")
|
2017-12-14 11:03:41 +08:00
|
|
|
}
|
|
|
|
|
if let data = document.data(with:SnapshotOptions.serverTimestampBehavior(.estimate)) {
|
|
|
|
|
print("Read document: \(data)")
|
|
|
|
|
}
|
|
|
|
|
if let foo = document.get("foo") {
|
|
|
|
|
print("Field: \(foo)")
|
|
|
|
|
}
|
|
|
|
|
if let foo = document.get("foo", options: SnapshotOptions.serverTimestampBehavior(.previous)) {
|
|
|
|
|
print("Field: \(foo)")
|
|
|
|
|
}
|
|
|
|
|
// Fields can also be read via subscript notation.
|
2017-10-03 08:55:22 -07:00
|
|
|
if let foo = document["foo"] {
|
|
|
|
|
print("Field: \(foo)")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// TODO(mikelehen): There may be a better way to do this, but it at least demonstrates
|
|
|
|
|
// the swift error domain / enum codes are renamed appropriately.
|
|
|
|
|
if let errorCode = error.flatMap({
|
|
|
|
|
($0._domain == FirestoreErrorDomain) ? FirestoreErrorCode (rawValue: $0._code) : nil
|
|
|
|
|
}) {
|
|
|
|
|
switch errorCode {
|
|
|
|
|
case .unavailable:
|
|
|
|
|
print("Can't read document due to being offline!")
|
|
|
|
|
case _:
|
|
|
|
|
print("Failed to read.")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
print("Unknown error!")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func readDocuments(matching query: Query) {
|
|
|
|
|
query.getDocuments() { querySnapshot, error in
|
|
|
|
|
// TODO(mikelehen): Figure out how to make "for..in" syntax work
|
|
|
|
|
// directly on documentSet.
|
|
|
|
|
for document in querySnapshot!.documents {
|
|
|
|
|
print(document.data())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func listenToDocument(at docRef: DocumentReference) {
|
|
|
|
|
|
2017-12-18 21:05:07 -08:00
|
|
|
let listener = docRef.addSnapshotListener() { document, error in
|
|
|
|
|
if let error = error {
|
|
|
|
|
print("Uh oh! Listen canceled: \(error)")
|
|
|
|
|
return
|
2017-10-03 08:55:22 -07:00
|
|
|
}
|
|
|
|
|
|
2017-12-18 21:05:07 -08:00
|
|
|
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();
|
2017-10-03 08:55:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2017-12-14 11:03:41 +08:00
|
|
|
// Note that document.data() is not nullable.
|
|
|
|
|
let data : [String:Any] = document.data()
|
|
|
|
|
print("Doc: ", data)
|
2017-10-03 08:55:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unsubscribe
|
|
|
|
|
listener.remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func listenToQueryDiffs(onQuery query: Query) {
|
|
|
|
|
|
|
|
|
|
let listener = query.addSnapshotListener() { snap, error 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 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
|
|
|
|
|
|
2017-12-18 21:05:07 -08:00
|
|
|
if balanceA < amount {
|
2017-10-03 08:55:22 -07:00
|
|
|
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
|
|
|
|
|
}) { (result, error) in
|
|
|
|
|
// handle result.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func types() {
|
|
|
|
|
let _: CollectionReference;
|
2017-10-04 20:45:10 -07:00
|
|
|
let _: DocumentChange;
|
|
|
|
|
let _: DocumentListenOptions;
|
2017-10-03 08:55:22 -07:00
|
|
|
let _: DocumentReference;
|
|
|
|
|
let _: DocumentSnapshot;
|
2017-10-04 20:45:10 -07:00
|
|
|
let _: FieldPath;
|
|
|
|
|
let _: FieldValue;
|
|
|
|
|
let _: Firestore;
|
|
|
|
|
let _: FirestoreSettings;
|
|
|
|
|
let _: GeoPoint;
|
2017-10-06 09:48:03 -07:00
|
|
|
let _: ListenerRegistration;
|
2017-10-04 20:45:10 -07:00
|
|
|
let _: QueryListenOptions;
|
|
|
|
|
let _: Query;
|
2017-10-03 08:55:22 -07:00
|
|
|
let _: QuerySnapshot;
|
2017-10-06 09:48:03 -07:00
|
|
|
let _: SetOptions;
|
2017-10-04 20:45:10 -07:00
|
|
|
let _: SnapshotMetadata;
|
|
|
|
|
let _: Transaction;
|
|
|
|
|
let _: WriteBatch;
|
2017-10-03 08:55:22 -07:00
|
|
|
}
|