UIKit update of SwiftUI @State causing "Modifying state during view update"

Viewed 764

I have a SwiftUI component loading a MapKit like this :

struct AddressView: View {

    @State private var showingPlaceDetails = false

    var body: some View {
        MapView(showPlaceDetails: self.$showPlaceDetails)
    }
}

The MapView component is a MapKit struct using UIKit -> SwiftUI wrapping technique :

struct MapView: UIViewRepresentable {

    @Binding var showingPlaceDetails: Bool

    func makeUIView(context: Context) -> MKMapView {

        let map = MKMapView()

        map.delegate = context.coordinator

        return map
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    final class Coordinator: NSObject, MKMapViewDelegate {

        var control: MapView

        init(_ control: MapView) {
            self.control = control
        }

        func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {

              // This is where the warning happen
              self.control.showingPlaceDetails = true
        }
    }
}

So mutating showPlaceDetails is firing this warning : [SwiftUI] Modifying state during view update, this will cause undefined behavior.

How should i clean this code ? Is this implementation correct ?

I understand that i am changing thru a @Binding a parent @State property that will re-render AddressView with MapView.

I understand why its bad, like to change a state property inside a render in React and that this mutation should occur outside the body but how can it be done as i need MapView inside the body ?

XCode Version 11.3.1 (11C504)

macOS Version 10.15.4 Beta (19E224g)

1 Answers

The usual fix for this is pending modification, like below

    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
          DispatchQueue.main.async {
             self.control.showingPlaceDetails = true
          }
    }
Related