Learn Courses My Dashboard

Completion handler isn't executed

Hello everyone, I’m trying to attach the recipe list app to a Firebase Firestore database.

Everything works fine including my function to add the recipe data to the database.

But when I try to retrieve the documents from the database and try to convert them into objects of my

Recipe structure it isn’t working(I use my getData() method for this). I think this is because the

.getDocuments completion handler isn’t executed but I’m not sure.

Why isn’t my function working?

Here is my code.

This is my Recipe structure:

This is my getData() function:

When posting code to these forums, please post the code as text rather than an image. This makes it far easier to read (some of us have aging eyes!) and also makes it possible for other posters to copy/paste the code in order to test solutions and such without having to retype all your code from scratch.

To post your code as text, place three backticks ``` on the line before your code and three backticks ``` on the line after your code so that it will be formatted properly. You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.

Yes please follow what @roosterboy wrote about pasting code in the form.

Have you added a breakpoint to see if the code goes into the completion handler? Or are you getting the error to print out?

Okay thanks for your tip, I didn’t know this at frist.

Here is my code:


// recipe.swift

// recipe list app


// Created by Rune Pollet on 25/01/2022.


**import** Foundation

**struct** Recipe: Identifiable {

**var** id:String

**var** name:String

**var** featured:Bool

**var** image:String

**var** description:String

**var** prepTime:String

**var** cookTime:String

**var** totalTime:String

**var** servings:Int

**var** highlights:[String]

**var** ingredients:[Ingredient]

**var** directions:[String]


**struct** Ingredient: Identifiable {

**var** id:String

**var** name:String

**var** num:Int?

**var** denom:Int?

**var** unit:String?

func getData() {
        // Properties for the database and the recipes collection
        let db = Firestore.firestore()
        let recipes = db.collection("recipes")
        // Get all the documents in the recipes collection
        recipes.getDocuments { querySnapshot, error in
            if error == nil && querySnapshot != nil {
                // Property for the array of books
                var recipes = [Recipe]()
                for doc in querySnapshot!.documents {

                    // Append the book to the "booksOfGenre" array
                    let id = doc.documentID
                    let name = doc["name"] as? String ?? Defaults.defaultName
                    let featured = doc["featured"] as? Bool ?? Defaults.defaultFeatured
                    let image = doc["image"] as? String ?? Defaults.defaultImage
                    let description = doc["description"] as? String ?? Defaults.defaultDescription
                    let prepTime = doc["prepTime"] as? String ?? Defaults.defaultTime
                    let cookTime = doc["cookTime"] as? String ?? Defaults.defaultTime
                    let totalTime = doc["totalTime"] as? String ?? Defaults.defaultTime
                    let servings = doc["servings"] as? Int ?? Defaults.defaultServings
                    let highlights = doc["highlights"] as? [String] ?? Defaults.defaultHighlights
                    let ingredients = [Ingredient]()
                    let directions = doc["directions"] as? [String] ?? Defaults.defaultDirections
                    // Append a Recipe with the values
                    recipes.append(Recipe(id: id, name: name, featured: featured, image: image, description: description, prepTime: prepTime, cookTime: cookTime, totalTime: totalTime, servings: servings, highlights: highlights, ingredients: ingredients, directions: directions))
                // Output the data
                self.data = recipes
            else {
                // Print the error

I hope this is better.

Yes, I have but the breakpoint is skipped and the project stop when it sees it doesn’t have any data to work with.

What do you mean it’s skipped but then it does stop?
Add a breakpoint to:
let id = doc.documentID

And in the console type po doc.documentID

Is anything printed?

When I change the view code of the app to just a text element, the breakpoint isn’t skipped and I can print doc.documentID

But when I add all the view code I get an index out of range error when I try to loop through my recipes array (This is where my data should be stored). When I print out my recipes array, it says that there are zero elements.

I have done some more work and it works now.

I think the project was reading all of the view code before executing the completion handler so I have just added an if statement that check if there is data or not.

After the view code is read, the completion handler is executed and everything works fine.

Thank you all for helping me out!

1 Like

It’s great that you solved the issue largely by yourself. I’m sure there is a greater sense of satisfaction when you achieve that. Well done.

1 Like