Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

How to correctly set a Picker's selection and contents in SwiftUI for macOS?

How do you atomically set a Picker's selection and contents on macOS such that you don't end up in a situation where the selection is not present within the Picker's content?

I presume Picker on macOS is implemented as an NSPopUpButton and an NSPopUpButton doesn't really like the concept of "no selection". SwiftUI, when presented with that, outputs:

Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results.

Consider the following pseudo code:

struct ParentView: View { 
  @State private var items: [Item]

  var body: some View { 
    ChildView(items: items)
  }
}

struct ChildView: View { 
  let items: [Item]
  @State private var selectedItem: Item?

  var body: some View { 
    Picker("", selection: $selectedItem) { 
      ForEach(items) { item in
        Text(item.name).tag(item)
      }
    }
  }
}

When items gets passed down from ParentView to the ChildView, it's entirely possible that the current value in selectedItem represents an Item that is not longer in the items[] array.

You can "catch" that by using .onAppear, .task, .onChange and maybe some other modifiers, but not until after at least one render pass has happened and an error has likely been reported because selectedItem is nil or it's not represented in the items[] array.

Because selectedItem is private state, a value can't easily be passed down from the parent view, though even if it could that just kind of moves the problem one level higher up.

What is the correct way to handle this type of data flow in SwiftUI for macOS?

Answered by DTS Engineer in 838510022

Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results.

on macOS, this seems to be specific to the default pickerStyle which is a menu.

The error message hints at possible ways to handle this. You can provide an initial selection value and a default value if that item gets removed from the collection.

For example:

struct ChildView: View {
    var items: [Item]
    @State private var selectedItem: Item?

    private var selection: Binding<Item?> {
        Binding(
            get: {
                items.first(where: { $0 == selectedItem }) ?? items.first
            },
            set: { newItem in
                selectedItem = newItem
            }
        )
    }
    var body: some View {
        Picker("Test", selection: selection) {
            ForEach(items) { item in
                Text(item.name)
                    .tag(item, includeOptional: true)
            }
        }
    }
}

Another option is to use a Menu instead If it’s multiple selection or if you want the default picker style.

Accepted Answer

Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results.

on macOS, this seems to be specific to the default pickerStyle which is a menu.

The error message hints at possible ways to handle this. You can provide an initial selection value and a default value if that item gets removed from the collection.

For example:

struct ChildView: View {
    var items: [Item]
    @State private var selectedItem: Item?

    private var selection: Binding<Item?> {
        Binding(
            get: {
                items.first(where: { $0 == selectedItem }) ?? items.first
            },
            set: { newItem in
                selectedItem = newItem
            }
        )
    }
    var body: some View {
        Picker("Test", selection: selection) {
            ForEach(items) { item in
                Text(item.name)
                    .tag(item, includeOptional: true)
            }
        }
    }
}

Another option is to use a Menu instead If it’s multiple selection or if you want the default picker style.

How to correctly set a Picker's selection and contents in SwiftUI for macOS?
 
 
Q