Child view does at first display when row selected in parent view

I am working on my first app to display a list of meals and their details. I have simplified the code for my question here but essentially a list of meals is presented and I should be able to click on one to go to a child view. I am experiencing a weird behavior where the first time I click on a meal the child comes up blank. If I then click on another meal and click back. the child view displays fine. any help you can give me would be greatly appreciated.

import SwiftUI

struct ContentView: View {

@State var meals: [Meal] = [
    Meal(name: "filet steak"),
    Meal(name: "pepperoni pizza"),
    Meal(name: "pancakes"),
    Meal(name: "full breakfast")
]
@State private var selectedMeal: Meal?
@State private var isShowingMealForm = false

var body: some View {
    NavigationStack {
        List {
                ForEach(meals) { meal in
                    HStack {
                        Text(meal.name)
                            .font(.headline)
                    }
                    .onTapGesture {
                        self.selectedMeal = meal
                        isShowingMealForm = true
                        
                    }
                }
            }
        }
    
        .sheet(isPresented: $isShowingMealForm) {
            if let m = selectedMeal {
                Text(m.name)
            }
        }
    }
}

class Meal: Identifiable { var id: UUID = UUID() var name: String = ""

init() {}

init(id: UUID = UUID(),  name: String)
{
    self.id = id
    self.name = name
    
}

}

I tried your code and found the same thing. These posts talk about some similar issues. https://forums.developer.apple.com/forums/thread/661777 https://forums.developer.apple.com/forums/thread/659660 this one has a solution that worked https://forums.developer.apple.com/forums/thread/714912?answerId=728415022#728415022

here's your code, working (based on the solution in the last link above), but it doesn't look like your original implementation:

    @Environment(\.dismiss) var dismiss

    var mealName: String

    var body: some View {
        VStack {
            Text(mealName)
            Button("OK") {
                dismiss()
            }
        }
        .padding()
    }
}

struct ContentView: View {
    @State var meals: [Meal] = [
        Meal(name: "filet steak"),
        Meal(name: "pepperoni pizza"),
        Meal(name: "pancakes"),
        Meal(name: "full breakfast")
    ]
    @State private var selectedMeal: Meal?

    var body: some View {
        NavigationStack {
            List {
                ForEach(meals) { meal in
                    HStack {
                        Button {
                         selectedMeal = meal
                        } label: {
                            Text(meal.name)
                                .font(.headline)
                        }
                    }
                }
                
            }
            .sheet(item: $selectedMeal) { item in
                SheetView(mealName: item.name)
            }
        }
    }
}


class Meal: Identifiable {
    var id: UUID = UUID()
    var name: String = ""
    
    init() {}

    init(id: UUID = UUID(), name: String) {
        self.id = id
        self.name = name
    }
}

that is, it has buttons not just simply lines of text.

here's code that works around the issue another way, by passing a binding to the sheet and using a small delay before changing isShowingMealForm.

    @Environment(\.dismiss) var dismiss
    
    @Binding var mealName: String
    
    var body: some View {
        VStack {
            Text(mealName)
            Button("OK") {
                dismiss()
            }
        }
        .padding()
    }
}

struct ContentView: View {
    @State var meals: [Meal] = [
        Meal(name: "filet steak"),
        Meal(name: "pepperoni pizza"),
        Meal(name: "pancakes"),
        Meal(name: "full breakfast")
    ]
    @State private var isShowingMealForm = false
    @State private var selectedMeal: Meal?
    @State private var mealName: String = "no meal selected"

    var body: some View {
        NavigationStack {
            List {
                ForEach(meals) { meal in
                    HStack {
                        Text(meal.name)
                            .font(.headline)
                    }
                    .onTapGesture {
                        if self.isShowingMealForm {
                            self.isShowingMealForm = false
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
                                self.selectedMeal = meal
                                self.mealName = meal.name
                                self.isShowingMealForm = true
                            }
                        } else {
                            self.selectedMeal = meal
                            self.mealName = meal.name
                            self.isShowingMealForm = true
                        }
                    }
                }
            }
        }
        .sheet(isPresented: $isShowingMealForm) {
            SheetView(mealName: $mealName)
        }
    }
}

class Meal: Identifiable {
    var id: UUID = UUID()
    var name: String = ""

    init() {}

    init(id: UUID = UUID(), name: String) {
        self.id = id
        self.name = name
    }
}

This works too, but I am a loss to clearly explain why. I put a breakpoint in your original onTapGesture closure, and I can clearly see self.selectedMeal obstinately staying at nil even after the line self.selectedMeal = meal. It only works as expected after you click on another meal line.

I'm not at all comfortable with arbitrary delays in code, and the version with .sheet(item) has fewer lines of code than the one with .sheet( isPresented: ), so I'd go with that.

I'd love it if someone who understands this stuff could chime in and tell us why the observed behavior differs from the expected though.

Thanks. I ended up using destination links as a workaround instead. Would still be curious to know why it doesn't work :)

this seemed to work somehow.

.onTapGesture { selectedMeal=meal } .onChange(of: selectedMeal) { newValue in if newValue != nil { isShowingMealForm = true } }

Child view does at first display when row selected in parent view
 
 
Q