Learn Courses My Dashboard

Module 4 Lesson 8 - Weird behavior of navigation after first click

When I click one of the items for the first time, it briefly shows the detailed view and then come back. After that, the app seems to freeze. Here’s the scree cap. Notice there is a clipped view at the bottom. What’s going on here?

And here’s the code

The way navigation works on an iPhone changed in iOS 15. Add the following modifier to the closing brace of your NavigationView in that code.

.navigationViewStyle(.stack)

Thanks Chris. Unfortunately, that still didn’t solve the problem, I really like to understand what’s going on here. I think it’s a good learning opportunity. FYI, I even copy-pasted all the codes from the resource just to be sure. Could there be something different about this project because I see some words colored differently. Just weird

Can you share your project by compressing it at the root project level (compress the folder which contains your projectName.xcodeproj file and the folder of the same name).

Post that to DropBox and create a share link. Paste that share link in a reply.

OK let me have a look see at what is going on.

(edit). Hmmmm, first question is, what version of Xcode are you using?

@chapstick

OK the issue was with the way you were assigning the UUID for the id values in the Model. Since id does not exist as one of the properties of the JSON file then it has to be declared as an optional in the Model. IE:

class Recipe: Identifiable, Decodable {
    
    var id: UUID?  // Optional
    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]
    
}

class Ingredient : Identifiable, Decodable {
    var id: UUID?  //  Optional
    var name:String
    var num:Int?
    var dnom:Int?
    var unit:String?
}

Then when you decode the JSON file in your DataService class function getLocalData() you need to assign that UUID manually after the JSON file has been decoded by looping through the records like this:

                let recipeData = try decoder.decode([Recipe].self, from: data)

                //  Assign UUID to 'id' optional values in the Model for each recipe and each ingredient.
                for recipe in recipeData {
                    recipe.id = UUID()
                    for ingredient in recipe.ingredients {
                        ingredient.id = UUID()
                    }
                }

That would have been the last place I"m going to look at since I’ve been using derived property ever since for my model. I’ve always thought the iteration just to assign an ID is inefficient. Now I know a compelling reason not to do that anymore. Thanks so much for all the help!

Specifically, because you implemented id as a computed property, a new id was assigned every time the property was accessed. And when a View gets a new id, it gets re-rendered. Which means your detail screen would jump back to the list screen being re-rendered.

But you don’t need to make it an optional, you can actually assign it when the object is created. Like so:

class Recipe: Identifiable, Decodable {
    let id = UUID()
    ...
}

Xcode will flag this with a warning that id can’t be decoded because it’s an immutable property that has an initial value, but it’s a silly warning for perfectly valid code.

And if that warning really bothers you, you can declare a CodingKeys enum listing all the properties except id.

I misunderstood the purpose of that feature. I was looking for something as convenient as C# object initializer for readonly property like ID - the object modeler side of me is kicking in :slight_smile: I can definitely do a {get;private set} here but too much for its purpose now. Thanks again for the education.