When i try to fetch a non-existent Firestore document my app crashes
The Firestore documentation says that I should just get a null document, not a terminal error https://firebase.google.com/docs/firestore/query-data/get-data
Note: If there is no document at the location referenced by docRef, the resulting document will be null.
my code to replicate the error is as follows. For this run, just using the two of the simple buttons. The first button (doReadDeepDocument) reads the document to make sure it does exist. The second button (doDeleteDeepDocument) deletes the document. Note that the document is in a subcollection.
import UIKit
import Firestore
class ViewController: UIViewController {
var listenerFriends: FIRListenerRegistration?
@IBAction func doReadDeepDocument(_ sender: Any) {
print("\nDo Read")
let db = Firestore.firestore()
let docRef = db.collection("basic").document("c9qjeO2uSXhPWejYIQ5H").collection("deep").document("4ZgsZJ1xQ3NfDj6BTv3H")
docRef.getDocument { (document, error) in
if error != nil {
print("Error reading the document \(error.debugDescription)")
return
}
print("Inside read completion")
if let document = document {
print("SUCCESS GET Document data: \(document.data())")
} else {
print("ERROR Document does not exist")
}
}
}
@IBAction func doDeleteDeepDocument(_ sender: Any) {
print("\nDo Delete")
let db = Firestore.firestore()
let docRef = db.collection("basic").document("c9qjeO2uSXhPWejYIQ5H").collection("deep").document("4ZgsZJ1xQ3NfDj6BTv3H")
docRef.delete() { err in
print("Inside delete completion")
if let err = err {
print("Error removing document: \(err)")
} else {
print("Document successfully removed!")
}
}
}
@IBAction func doBasicWrite(_ sender: Any) {
var values = [String : Any]()
values["fld1"] = 1
values["fld2"] = "Two"
let db = Firestore.firestore()
db.collection("basic").document().setData(values) { (error) in
if error != nil {
print("basic write failed \(error.debugDescription)")
} else {
print("basic write success")
}
}
}
@IBAction func doReadBasicSet(_ sender: Any) {
let db = Firestore.firestore()
db.collection("basic").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
print("Success getting documents")
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
}
@IBAction func doDeepWrite(_ sender: Any) {
var values = [String : Any]()
values["fldA"] = 1
values["fldB"] = "BBB"
let db = Firestore.firestore()
db.collection("basic").document("c9qjeO2uSXhPWejYIQ5H").collection("deep").document().setData(values) { (error) in
if error != nil {
print("deep write failed \(error.debugDescription)")
} else {
print("deep write success")
}
}
}
@IBAction func doReadDeepSet(_ sender: Any) {
let db = Firestore.firestore()
db.collection("basic").document("c9qjeO2uSXhPWejYIQ5H").collection("deep").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
print("Success getting documents")
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
}
@IBAction func doStartListen(_ sender: Any) {
// Get all my friends
let db = Firestore.firestore()
let deepRef = db.collection("basic").document("c9qjeO2uSXhPWejYIQ5H").collection("deep")
// start listener
listenerFriends = deepRef.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
self.watchFriend_Added( diff : diff )
}
if (diff.type == .modified) {
// could be a case where it is marked as 'modified' but is really 'new'
// reletive to the start of the listener
// e.g. it was recently changed but prior to the start of the listen.
self.watchFriend_Modified( diff : diff )
}
if (diff.type == .removed) {
self.watchFriend_Removed( diff : diff )
}
}
}
}
func watchFriend_Added( diff : DocumentChange) {
print("\nNew friend: \(diff.document.data())")
print("ID: \(diff.document.documentID)")
}
func watchFriend_Modified( diff : DocumentChange) {
print("\nModified friend: \(diff.document.data())")
print("ID: \(diff.document.documentID)")
}
func watchFriend_Removed( diff : DocumentChange) {
print("\nRemoved friend: \(diff.document.data())")
print("ID: \(diff.document.documentID)")
}
@IBAction func doStopListen(_ sender: Any) {
// Stop listening for changes
if listenerFriends != nil {
listenerFriends?.remove()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
-
Below is the debug console results. You can see the successful read (read button pressed). This confirms the data initially exists and the path is correct. Then you see the successful delete (delete button pressed). Then I push the read again and the app crashes.
Do Read
Inside read completion
SUCCESS GET Document data: ["fldB": EEE, "fldA": 1]
Do Delete
Inside delete completion
Document successfully removed!
Do Read
Inside read completion
2018-03-03 12:09:44.471079-0800 testfs[32914:2379972] *** Terminating app due to uncaught exception 'NonExistentDocumentException', reason: 'Document '<FSTDocumentKey: basic/c9qjeO2uSXhPWejYIQ5H/deep/4ZgsZJ1xQ3NfDj6BTv3H>' doesn't exist. Check document.exists to make sure the document exists before calling document.data.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010dce112b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010ce28f41 objc_exception_throw + 48
2 testfs 0x000000010aaacf0b -[FIRDocumentSnapshot objectForKeyedSubscript:] + 0
3 testfs 0x000000010a9ddce4 _T06testfs14ViewControllerC18doReadDeepDocumentyypFySo0G8SnapshotCSg_s5Error_pSgtcfU_ + 1188
4 testfs 0x000000010a9ddfbc _T0So16DocumentSnapshotCSgs5Error_pSgIxxx_ACSo7NSErrorCSgIyByy_TR + 124
5 testfs 0x000000010aaac2bb __50-[FIRDocumentReference getDocumentWithCompletion:]_block_invoke + 357
6 testfs 0x000000010aaac878 __72-[FIRDocumentReference addSnapshotListenerInternalWithOptions:listener:]_block_invoke + 305
7 libdispatch.dylib 0x0000000110f352f7 _dispatch_call_block_and_release + 12
8 libdispatch.dylib 0x0000000110f3633d _dispatch_client_callout + 8
9 libdispatch.dylib 0x0000000110f415f9 _dispatch_main_queue_callback_4CF + 628
10 CoreFoundation 0x000000010dca3e39 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
11 CoreFoundation 0x000000010dc68462 __CFRunLoopRun + 2402
12 CoreFoundation 0x000000010dc67889 CFRunLoopRunSpecific + 409
13 GraphicsServices 0x000000011423e9c6 GSEventRunModal + 62
14 UIKit 0x000000010e15f5d6 UIApplicationMain + 159
15 testfs 0x000000010a9e54f7 main + 55
16 libdyld.dylib 0x0000000110fb2d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Do I need to trap for fatal errors of GetDocument() everywhere used or is this a bug that will be fixed. Or, some other suggestion?
Not at Xcode, but the following should work:
if let document = document, document.exists {
print("SUCCESS GET Document data: \(document.data())")
} else {
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With