Along with the updated sidebar design introduced this year at WWDC, Apple gave us the ability to add multidimensional lists - that is - a list who's tree structure is recursive,  in even simpler terms, multiple submenus. At the time of writing you'll need Xcode 12 beta 1 for this code to run.

Let's see an example using a menu with 3 levels, the structure looks like this.

Section
--- Menu Item
------ Menu Item
struct Menu: Identifiable {
    var id = UUID()
    let name: String
    let icon: String
    var children: [Menu]?
    
    init(name: String, icon: String, children: [Menu]? = nil) {
        self.name = name
        self.icon = icon
        self.children = children
    }
}

let menuItems: [Menu] = [
    Menu(name: "Section 1", icon: "", children: [
        Menu(name: "Sub section 1", icon: "folder", children : [
            Menu(name: "Detail 1", icon: "circle"),
            Menu(name: "Detail 2", icon: "circle"),
            Menu(name: "Detail 3", icon: "circle")
        ]),
        Menu(name: "Sub section 2", icon: "pencil", children : [
            Menu(name: "Detail 4", icon: "square"),
            Menu(name: "Detail 5", icon: "square")
        ])
    ]),
    Menu(name: "Section 2", icon: "", children: [
        Menu(name: "Sub section 3", icon: "paperplane.fill", children : [
            Menu(name: "Detail 6", icon: "circle"),
            Menu(name: "Detail 7", icon: "circle"),
            Menu(name: "Detail 8", icon: "circle")
        ]),
        Menu(name: "Sub section 4", icon: "archivebox", children : [
            Menu(name: "Detail 9", icon: "square"),
            Menu(name: "Detail 10", icon: "square")
        ])
    ]),
    Menu(name: "Section 3", icon: "doc", children: [
        Menu(name: "Sub section 3", icon: "doc")
    ])
]

Here we have a simple Menu model with an id, name, icon and children. We then create an array that represents our desired menu structure, as you can see we have Sections at the top level, with an array of children to represent our collapsable menu sections, then another array of children to represent our final menu items.

struct SideBar: View {
    
    var body: some View {
        List {
            ForEach(menuItems) { menuItem in
                Section(header: Text(menuItem.name)) {
                    OutlineGroup(menuItem.children ?? [Menu](),
                                 children: \.children) { child in
                        Label(child.name, systemImage: child.icon)
                    }
                }
            }
        }
        .listStyle(SidebarListStyle())
    }
}

Next we want to create a List and iterate over this structure in a ForEach, then use Section to render our headers. We then move onto OutlineGroup, this takes in our array of children and the key path to the children array in the menu model. OutlineGroup is called recursively until there are no more children and renders a nice collapsable group.

To avoid nil coalescing here (??) we could create a container model who's children is a non optional array of type Menu

In this example we've used a List, but OutlineGroup can be used independently, another great use case is using it inside the new Form View in SwiftUI. This can be as easy as changing List to Form in the above example.

Thanks for reading ✌️