iosswiftuiswiftui-navigationview

in navigationDestination(item:destination), what is the best to way to convert item into a binding


In my app, I have:

.navigationDestination(item: self.$selectedProject) { selectedProject in
    ProjectView(project: Binding<Project>(
        get: { selectedProject },
        set: { self.selectedProject = $0 }
    ))
}

Where selectedProject is a property in my view declared as @State private var selectedProject: Project? (Project is a model object).

I'm simply wanting to unwrap self.selectedProject into a Binding<Project> and send it as a parameter to ProjectView.

The above solution is the best I've come up with so far, but passing the closure parameter selectedProject for the get: is bugging me because it only captures a local copy. I also don't want to use self.selectedProject! for the get: as I try to avoid force unwrapping.

I've also tried doing it the conventional way:

.navigationDestination(item: self.$selectedProject) { _ in
    if let selectedProject = Binding(self.$selectedProject) {
        ProjectView(project: selectedProject)
    }
    else {
        Helpers.errorText()
    }
}

Weirdly though, it's crashing with an

EXC_BREAKPOINT

when I hit the back button on ProjectView to return to this current view that has the .navigationDestination code.

I'm just wondering what the safest and best way is to do this.


Solution

  • You could try a different approach using .navigationDestination(isPresented: $showProjectView), as shown in this example code:

    
    struct Project: Identifiable, Hashable {
        let id = UUID()
        var name: String
    }
    
    struct ContentView: View {
        @State private var selectedProject: Project?
        @State private var showProjectView = false
        
        var body: some View {
            Text("selected project: \(selectedProject?.name ?? "none")")
                .padding(30)
                .border(.green)
    
            NavigationStack {
                Button("testing") {
                    if selectedProject == nil {
                        selectedProject = Project(name: "Mickey Mouse")
                    }
                    showProjectView = true
                }
                .navigationDestination(isPresented: $showProjectView) {
                    if let binding = Binding($selectedProject) {
                        ProjectView(project: binding)
                    } else {
                        Text("No project selected")
                    }
                }
            }
        }
    }
    
    struct ProjectView: View {
        @Binding var project: Project
        
        var body: some View {
            TextField("Project name", text: $project.name).border(.red)
        }
    }