I like how the TabView
control looks on the mac and ipad and decided to see if my current code can show the tabs in my multi-platform app just to realize that whenever I click on one of those tabs my macOS app currently crashes every time I press any other tab with the error: Thread 1: "NSToolbar 0x600003de33c0 already contains an item with the identifier com.apple.SwiftUI.navigationSplitView.toggleSidebar. Duplicate items of this type are not allowed."
While trying to troubleshoot I noticed several other people have had a similar issue with differing reasons (toolbars and searchers mentioned) all in macOS since upgrading to 15.0: https://forums.developer.apple.com/forums/thread/763829
Minimal Viable Project: to show the issue
I commented out most of my code calls hoping to create a project that worked so I could bring my code back in and see if it broke. I still had this issue so I next created a minimal viable example.
Here it is:
import SwiftUI
import SwiftData
@Model
class Issue {
var name: String
init(name: String) {
self.name = name
}
}
@main
struct TestMultiplatformApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Issue.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
struct ContentView: View {
@State var isCompact: Bool = true
var body: some View {
VStack {
if isCompact {
EntryTab()
} else {
EntrySidebar()
}
Toggle(isOn: $isCompact, label: {
Text(isCompact ? "TabView" : "Sidebar")
})
.padding(.horizontal, 20)
.padding(.bottom, 20)
}
}
}
public struct tabControl: Identifiable, Hashable, Sendable {
public static func == (lhs: tabControl, rhs: tabControl) -> Bool {
lhs.id < rhs.id
}
public var id: Int // Tab Number
public var displayName: String
public init(id: Int, displayName: String) {
self.id = id
self.displayName = displayName
}
}
struct EntryTab: View {
let entryTabs = [
tabControl(id: 0, displayName: "row 0"),
tabControl(id: 1, displayName: "row 1"),
tabControl(id: 2, displayName: "row 2"),
tabControl(id: 3, displayName: "row 3")
]
@State private var selectedTab: Int = 0
var body: some View {
TabView(selection: $selectedTab) {
ForEach(entryTabs) { tabCtrl in
NavigationSplitView {
Text("Selected tab is \(selectedTab)")
} detail: {
Text("Choose item from sidebar... in future this would be content")
}
.tabItem {
Text(tabCtrl.displayName)
}
.tag(tabCtrl.id)
}
}
}
}
struct EntrySidebar: View {
@State private var selectedTabID: Int?
let entryTabs = [
tabControl(id: 0, displayName: "row 0"),
tabControl(id: 1, displayName: "row 1"),
tabControl(id: 2, displayName: "row 2"),
tabControl(id: 3, displayName: "row 3")
]
var body: some View {
NavigationSplitView(sidebar: {
List(entryTabs, id:\.id, selection: $selectedTabID) { thisItem in
Text(thisItem.displayName)
}
}, content: {
Text("Hi selected tab: \(String(describing: selectedTabID))")
}, detail: {
Text("Choose item from sidebar... in future this would be content")
})
.onAppear() {
// Set the selected tab
selectedTabID = 1
}
}
}