Supporting SwiftUI Sidebar Selection with Multiple Data Types
When handling the sidebar selection in a SwiftUI app, the usual approach is to add a @State property to the view to store the selection. The code looks similar to the following code:
@State private var selection: Chapter? = nil
The usual approach works well if all the items in the sidebar have the same data type. What do you do if the sidebar items have different data types?
Supporting selection with multiple data types involves the following tasks:
- Create an enum for the sidebar selection.
- Use the enum you create as the data type for the selection.
- Add a
.tagmodifier to each list item that holds the item’s data type and value.
Creating the enum
Create an enum for the sidebar selection with a case for each possible data type a sidebar item can have.
As an example I’m going to show code for an app I’m currently making, a Mac GUI client for the Jujutsu version control system. The app’s sidebar has multiple items for controlling what appears in the list of changes (commits). The selection can be either a change category (all changes, mutable changes, my changes) or a bookmark (branch). Selecting a bookmark shows its changes.
The following code shows the enum for the sidebar selection:
enum SidebarSelection: Hashable, Sendable {
case change(category: ChangeCategory)
case bookmark(bookmark: Bookmark)
}
enum ChangeCategory: CaseIterable {
case allChanges
case mutableChanges
case myChanges
func stringValue() -> String {
switch(self) {
case .allChanges:
return "All Changes"
case .mutableChanges:
return "Mutable Changes"
case .myChanges:
return "My Changes"
}
}
}
struct Bookmark: Equatable, Hashable, Codable, Sendable {
// Other properties removed for clarity.
var name: String
static func == (lhs: Bookmark, rhs: Bookmark) -> Bool {
lhs.name == rhs.name
}
}
Using the enum for the selection
In the sidebar view, add a property to store the selection. The data type is the enum you created.
@State private var selection: SidebarSelection?
Adding the .tag modifier
Each list item in the sidebar needs a .tag modifier so SwiftUI knows what the item represents. The value of the modifier is one of the sidebar selection enum’s cases. You must also supply any additional data the enum case requires.
The following code shows a sidebar list with sections for change categories and bookmarks:
List(selection: $selection) {
Section(header: Text("Changes")) {
ForEach(ChangeCategory.allCases, id:\.self) { category in
Text(category.stringValue())
.tag(SidebarSelection.change(category: category))
}
}
Section(header: Text("Bookmarks")) {
ForEach(bookmarks, id:\.name) { bookmark in
Text(bookmark.name)
.tag(SidebarSelection.bookmark(bookmark: bookmark))
}
}
}