0

I'm working with firebase and I have a function which returns a promise from a document reference.

func getCatalogItem(ref: DocumentReference) -> Promise<Catalog> {
        return Promise { seal in
        ref.getDocument() { result, err in
            if err != nil {
                seal.reject(err!)
            } else {
                let data = FirestoreDecoder()
                do {
                    let item = try data.decode(Catalog.self, from: result!.data()!)
                    seal.fulfill(item)
                } catch {
                print("Error")
                }
            }
        }
    }
}

It works fine but the only thing is the return type is a custom type which I decoded from the document reference call but the call itself is synchronous and it crashes my app with a nil error because the data isn't actually there yet.

Here's the line and function that produces the error (nil)

let item = try data.decode(Catalog.self, from: result!.data()!)

I tried making another function with a handler for the firebase function and passing that value to the decoder but that also returned nil, and (I'm using awaitkit/promisekit) I tried messing around with async/await but couldn't get that to work. How could I fix this?

Screenshot of the document in my database: screenshot of the document I'm trying to access

and then i have this:

let featureditem = Firestore.firestore().collection("catalog").document("TDS faded jeans")
self.featureditem.getDocument() { doc, err in
                            if doc == doc {
                                let item = try! FirestoreDecoder().decode(Catalog.self, from: doc!.data()!)
                                print("\(item)")

Which is also giving me nil.

4
  • I’d suggest you avoid using forced unwrapping operator (the !). Especially avoid using multiple times on one line, making it incredibly hard to figure out what failed. So, unwrap result first (e.g. guard let result = result else { print("result was nil"); seal.reject(...); return }. Then use the same pattern to unwrap whatever data() returned. Then look at what your decoder returned (which I’d rename from data to decoder to avoid confusion), if it got that far. Commented Jan 3, 2020 at 19:59
  • Unrelated, but you probably want to reject in your catch statement, e.g., seal.reject(error). And if you’re going to print anything, I’d probably print the actual error, not just the string literal "Error". Commented Jan 3, 2020 at 20:03
  • Ok I’ll definitely go back and do those I was more worried about why I’m getting nil when I’m not supposed to but your right maybe that’ll help with my debugging Commented Jan 3, 2020 at 20:14
  • Ahhh I found the problem it's in my coding keys but it wasn't raising an error when i forced unwrapped it. Thanks @Rob Commented Jan 4, 2020 at 16:15

2 Answers 2

1

You need to check if data() can return nil, since that's what it returns when there's no document found. According to the linked API documentation:

Retrieves all fields in the document as an NSDictionary. Returns nil if the document doesn’t exist.

Either that, or check exists before assuming there's data.

Sign up to request clarification or add additional context in comments.

6 Comments

I ruled that out since i know the data exists and it (should) always exists but I went and checked and for some reason my document is returning nil even though i can clearly see it in firestore and i already checked for typos
Since I can't see that myself, I'm skeptical. The Firestore SDK doesn't get these things wrong. Please edit the code to check for nil at each step instead of blindly assuming not nil. Also include a screenshot so we can see ourselves.
FWIW, I’d advise against checking for existence first. I suspect you added that to round out your answer, but as a general rule, it is almost always better to do the read operation and handle failures, rather than checking for existence first.
@Rob The exists check I refer to here is a direct indicator from the snapshot itself whether the snapshot has any data. It should always be true if the data is non-nil. It's not an asynchronous query.
I’m not worried about the asynchronous/synchronous behavior. It’s just that as a general rule, it’s almost always better to do the “try and handle failure” than it is the “check for existence and then try opening” pattern. 1. It’s more efficient; and 2. it solves race conditions in multithreaded environments (admittedly, unlikely to be an issue here). But if these comments/observations bother you, I’m happy to delete...
|
1

An unusual error where the particular document I was trying to access would return a nil value but trying with another document fix the problem, it may be because I named a field wrong and ended up deleting it and creating a new field with the correct name, but there's no way to truly know.

1 Comment

there did happen to be a mistake in my coding keys but that wasn't the underlying problem

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.