Firestore: How do I assign data from only 1 document to my Model? (Unexpected non-void return value in void function)

Hi code crew :slight_smile:

Although I am familiar working with documents, I noticed today my brain went blank when I tried working with 1 document only in Firestore.

I want to pull data from 1 document that has just 2 fields in it: avHeight and avWater. Both are numbers and I want to use them through my Model.

This is a snippet of my Model:

struct CareData {
    var avHeight: Int
    var avWater: Int
}

And here a snippet of my ViewModel:

@Published var careData = [CareData]()

func getCareData(documentName: String) -> Int {
        
    let collectionCare = database.collection("collection-name").document(documentName)
        
        // Get the document
        collectionCare.getDocument { snapshot, error in
            
            // Check for errors
            if error == nil {

                // No errors
                if let snapshot = snapshot {
                    
                   
                    return CareData(avHeight: (["avHeight"] as? Int)!, avWater: (["avWater"] as? Int)!) // error message
                   
                }
            }
            else {
                // Handle the error
            }
        }
    }

After return, I get this error message: Unexpected non-void return value in void function.
I thought only function() returns a void, but mine has an Integer defined as an output.

I am not sure if my function needs to be corrected or if I am approaching this all wrong. I modelled my function after the ones I learned in the Firebase courses here on CWC, but all of them worked with getDocuments, not getDocument.

Hope anyone here can help, as I didn’t find anything helpful on Stackoverflow or Google :confused:

This is doing multiple things

It’s trying to typecast for an Int and then is force unwrapping it

You could separate these steps to make it more clear to you

Also you should be returning CareData, not an Int, that’s how you’ve written the actual body of the function

It seems like you’re not using the snapshot properly? If you print it after the if let snapshot = snapshot what’s printed?

When I print the snapshot, it prints <FIRDocumentSnapshot: somenumber>. So I assume this one is working?
When I replace -> Int with -> CareData I still get the same error message.

What confuses me, is why I even need to define what I want to return here. We didn’t do that in the database course when retrieving the modules for the Learning App either and I thought this is very similar, except I use only 1 document and for the learning app we queried through multiple documents.

@liviusminimus

Hi Livia,

You have defined careData as an array. ie:

@Published var careData = [CareData]()

That being the case your getCareData function really should be written like this:

    func getCareData() {

        let collectionCare = database.collection("collection-name")

        // Get the document
        collectionCare.getDocuments { snapshot, error in

            // Check for errors
            if error == nil {

                // No errors
                if let snapshot = snapshot {

                    for doc in snapshot.documents {
                        let newItem = CareData(avHeight: doc["avHeight"] as? Int ?? 0, avWater: doc["avWater"] as? Int ?? 0)

                        self.careData.append(newItem)
                    }
                }
            }
            else {
                // Handle the error
            }
        }
    }

I tested that here on a Firebase project I use for testing purposes and it works after having created a collection named “collection-name” with the two fields named the same like this (which I am assuming is what you have ??):

I just refactored my entire function, as I seemed to be on the completely wrong path.
However, I am running into a new error message now that I don’t understand how to fix :slight_smile:

Trailing closure passed to parameter of type ‘FirestoreSource’ that does not accept a closure

func getCareData(documentName: String) {
        
        let collectionCare = database.collection("collection-name").document(documentName)
        
        // Get the Document
        collectionCare.getDocument { document, error in // new error! Yay...
            
            let avHeight = document!.get("avHeight")
            let avWater = document!.get("avWater")
                
            // add to Care Data Model
           return CareData(avHeight: avHeight, avWater: avWater)
        }
    }

I am trying hard to understand where I am passing a closure here and why it is not accepted. This must be for sure something obvious for anyone with more experience, but I still consider myself a beginner :slight_smile:

Can you provide a screenshot of your Firebase Console showing your collection and the two fields in that collection?

Apologies Chris, I just saw your reply after I added my recent update. Yes, my Firebase setup pretty much looks like this.

Your code works perfectly, so thank you big time for the help! :pray: Looking at it, it makes perfect sense to use a loop and append the results to the array. I didn’t even think about that, as I thought it’s just 2 values in 1 doc. Thank you big time, there is still a lot to learn for me :slight_smile:

Edit: here we go with the screenshot. Exactly what you posted.

Yes, Firebase is tricky to get your head around and it’s taken me months to get a handle on it.

I’m pretty sure that Chris Ching has had way more experience with it than I given that he has been a Developer a lot longer than me AND back in the days using Objective C.