Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firestore - GetDocument() crashes app if document does not exist

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?

like image 501
john Avatar asked Sep 11 '26 03:09

john


1 Answers

Not at Xcode, but the following should work:

    if let document = document, document.exists {
        print("SUCCESS GET Document data: \(document.data())")
    } else {
like image 94
David Berry Avatar answered Sep 12 '26 16:09

David Berry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!