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:

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))
    }
  }
}

Get the Swift Dev Journal Newsletter

Subscribe and get exclusive articles, a free guide on moving from tutorials to making your first app, notices of sales on books, and anything I decide to add in the future.

    We won't send you spam. Unsubscribe at any time.