title: Firestore - GetDocument() crashes app if document does not exist
body: <p><strong>When i try to fetch a non-existent Firestore document my app crashes</strong> </p>
  
  <p><strong>The Firestore documentation says that I should just get a null document, not a terminal error</strong> <a href="https://firebase.google.com/docs/firestore/query-data/get-data" rel="nofollow noreferrer">https://firebase.google.com/docs/firestore/query-data/get-data</a></p>
  
  <blockquote>
    <p>Note: If there is no document at the location referenced by docRef,
    the resulting document will be null.</p>
  </blockquote>
  
  <p>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.</p>
  
  <pre><code>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) =&gt; \(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) =&gt; \(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.
  }
  
  
  }
  </code></pre>
  
  <p>-</p>
  
  <p>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.</p>
  
  <pre><code>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 '&lt;FSTDocumentKey: basic/c9qjeO2uSXhPWejYIQ5H/deep/4ZgsZJ1xQ3NfDj6BTv3H&gt;' 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
  </code></pre>
  
  <p>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?</p>