Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Displaying an editing hierarchy in macOS

The SwiftUI Navigation structures work in ways that are not intuitive to me. For example, I am trying to display a set of data that represents rankings contained in a balloting system that I have created. The ballots all have candidates that are ranked from highest preference to lowest.

Normally, I try to work backwards in SwiftUI, so I built the ballot editor to take a binding to the ballot itself:

struct BallotEditor: View {
    @Binding var ballot: Election.Ballot
    var maxRank: Int
    
    var body: some View {
        VStack {
            ForEach($ballot.rankings) { $ranking in
                CandidateRankingPicker(maxRanking: maxRank, ranking: $ranking)
            }
        }
    }
}

This is embedded into a view with a list of ballots:

struct BallotsView: View {
    @Binding var document: ElectionDocument
        
    var body: some View {
        List($document.ballots) { $ballot in
            NavigationLink {
                BallotEditor(ballot: $ballot, maxRank: document.election.candidates.count)
                    .padding()
            } label: {
                BallotListElementView(ballot: ballot)
            }
        }
    }
}

This portion works in the editor. When the ballot is selected, the editor populates the selected candidate choices, and the editing works.

However, when I attempt to insert BallotsView into a TabView, the NavigationLink stops working as expected. I didn't think NavigationLink was the proper way to do this, but it had been working.

TabView {
    Tab("Ballots", systemImage: "menucard") {
        BallotsView(document: $document)
    }
    
    Tab {
        CandidateView() 
    } label: {
        Text("Candidates")
    }
    .tabViewStyle(.sidebarAdaptable)
}

This is my third iteration. I have tried using a List with selection, but in that case, I am unable to pass the binding to the detail view. I just don't understand how this works, and I am preparing a version in Cocoa so that I don't have to deal with it anymore.

Hi @mmuszynski ,

It doesn't seem like you're using a NavigationStack, which you would need for the navigation links to work. Can you try with that like:

struct BallotsView: View {
    @Binding var document: ElectionDocument
        
    var body: some View {
NavigationStack {
        List($document.ballots) { $ballot in
            NavigationLink {
                BallotEditor(ballot: $ballot, maxRank: document.election.candidates.count)
                    .padding()
            } label: {
                BallotListElementView(ballot: ballot)
            }
        }
    }
}
}
Displaying an editing hierarchy in macOS
 
 
Q