Multi Section Sidebar using List with selections for macOS

I'm trying to implement a 3 column NavigationSplitView in SwiftUI on macOS - very similar to Apple's own NavigationCookbook sample app - with the slight addition of multiple sections in the sidebar similar to how the Apple Music App has multiple sections in the sidebar.

Note: This was easily possible using the deprecated

NavigationLink(tag, selection, destination) API

The most obvious approach is to simply do something like:

		NavigationSplitView(sidebar: {
			List {
				Section("Section1") {
					List(section1, selection: $selectedItem1) {
						item in
						NavigationLink(item.label, value: item)
					}
				}
				Section("Section2") {
					List(section2, selection: $selectedItem2) {
						item in
						NavigationLink(item.label, value: item)
					}
				}
			}
		},
		content: {
			Text("Content View")
		}, detail: {
			Text("Detail View")
		})

But unfortunately, this doesn't work - it doesn't seem to properly iterate over all of the items in each List(data, selection: $selected) or the view is strangely cropped - it only shows 1 item. However if the 1 item is selected, then the appropriate bindable selection value is updated. See image below:

If you instead use ForEach for enumerating the data, that does seem to work, however when you use ForEach, you loose the ability to track the selection offered by the List API, as there is no longer a bindable selection propery in the NavigationLink API.

		NavigationSplitView(sidebar: {
			List {
				Section("Section1") {
					ForEach(section1) {
						item in
						NavigationLink(item.label, value: item)
					}
				}
				Section("Section2") {
					ForEach(section2) {
						item in
						NavigationLink(item.label, value: item)
					}
				}
			}
		},
		content: {
			Text("Content View")
		}, detail: {
			Text("Detail View")
		})

We no longer know when a sidebar selection has occurred.

See image below:

Obviously Apple is not going to comment on the expected lifespan of the now deprecated API - but I am having a hard time switching to the new NavigationLink with a broken sidebar implementation.

@EulerDev You would want to stucture your underlying data with a level of hierarchy which would allow you structure your list this way:

List {
     ForEach(company.departments) { department in
         Section(header: Text(department.name)) {
             ForEach(department.staff) { person in
                PersonRowView(person: person)
             }
         }
     }
 }

Please review: Represent data hierarchy with sections and let me know if you any further questions.

I don't think I did a good job at explaining the actual issue. I think I have come up with a good solution that works for the new @Observable macro and also does not rely on the deprecated NavigationLink(tag, selection) API.

The issue is this:

It is common to show multiple lists in multipanel sidebar apps (i.e., Apple Music, Apple Mail) - and one way of keeping track of selection changes of items in the sidebar was to use the now deprecated NavigationLink(tag, selection) API - as this could be used to bind to a variable in your appModel that keeps track of user selections in the Sidebar.

Now that that version of the NavigationLink API has been deprecated, it was not clear how to keep track of the current selected item in multi-section sidebar lists. (Like Apple Music, Apple Mail, etc). Your example is not helpful because it doesn't show how you would create a binding to some data to keep track of the currently selected item in the sidebar.

The NavigationCookbook SwiftUI sample app is not helpful either because it doesn't show use of a multi-section sidebar. It uses the List(selection, content) API and uses a binding variable for selection to keep track of the selected item in the sidebar - but doesn't show how this could be accomplished with multiple unrelated lists in the Sidebar.

The problem with multi section items (especially when there are multiple sections of completely unrelated data (i.e.,Apple Music App sidebar) - is you can no longer use List(selection, content) as it seems Lists within Lists are not supported?

It seems like the proper way to keep track of the currently selected sidebar item with modern @Observable data and Swift Concurrency is to use the Destination view to update the state variable for keeping track of what is selected in the Sidebar. You have to use both .onAppear{} and .onChange(of:) to track these changes but this seems more robust than the old NavigationLlink(selection, content) method.

I will post a full working skeleton below that I assume is the preferred way moving forward with the now deprecated NavigationLink APIs and the new @Observable macro with their modified getters and setters.

Multi Section Sidebar using List with selections for macOS
 
 
Q