MapKit - Map(selection:) sets selection to nil between changes

I'm wondering if anyone else has experienced this behaviour? I'm running iOS 18 Beta 3 from Xcode 16 Beta 4 and encountering this:

struct MyView: View {
    @State
    private var selection: String?

    let values: [String] = ["hello", "world"] // imagine these also have CLLocationCoordinates

    var body: some View {
        Map(selection: $selection) { 
            ForEach(values) { value in 
                Annotation(coordinate: value.coordinate) {
                    Text(value)
                        .tag(value)
                } 
            }
        }
        .onChange(of: selection) {
            print(selection)
        }
}

When I run this sample code, and select say; the "hello" annotation, immediately followed by the "world" annotation, I will see this in the console:

nil // initial state
hello // tap "hello" annotation
nil   // tap "world" annotation
world 

when I would instead expect to see this:

nil // initial state
hello // tap "hello" annotation
world // tap "world" annotation

MapKit seems to add an extra "step" to the selection process where it momentarily clears the selection... :thinking:

Is this expected behaviour?

1 Like

@Skwiggs I'm experiencing the same issue. Did you find a solution or workaround?

1 Like

Well... My real world setup differs from the example above in that my selection is a @Published var selection: String in an ObservableObject, which allows the following (very ugly) fix:

@Published var selection: String?

func observeMapSelection() {
    $selection
        .removeDuplicates()
        .debounce(for: 0.1, scheduler: RunLoop.main) // this is necessary because MapKit sets selection to nil when switching
        .sink { selection in 
            // If selection is nil here, 
            // that means the user definitely de-selected an 
            // annotation
        }
        .store(in: &subscribers)
}

I simply observe the published value using Combine with .debounce(for: 0.1) to filter out the nil values if they're immediately followed by a valid value... :melting_face: