Search code examples
swiftuimapkituiviewrepresentable

SwiftUI, can't change state from within delegate callback? (code attached)


Anyone able to spot why when I set the "firstPass" state variable in the "updateUIView" callback it does NOT set the state? As output I see:

First Pass1:  true
First Pass2:  true. // <= not set to false as expected

Also I do not in Xcode where I set this state "firstPass = false" that there is an Xcode warning here: "Modifying state during view update, this will cause undefined behavior."

import SwiftUI
import MapKit

struct GCMapView {
    @State var firstPass : Bool = true
   
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: GCMapView
        init(_ parent: GCMapView) {
            self.parent = parent
            super.init()
        }
    }
}

extension GCMapView : UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        let map = MKMapView()
        map.delegate = context.coordinator
        map.showsUserLocation = true
        return map
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        print("--- updateUIView ---")
        if firstPass {
            print("First Pass1: ", firstPass)
            firstPass = false.      // <<=== *** THIS DOES NOT WORK ****
            print("First Pass2: ", firstPass)
        } else {
            print("Subsequent Passes")
        }
    }
}

Solution

  • This operation causes cycling, because it invalidates SwiftUI view that result in calling updateUIView and so forth, so SwiftUI rendering engine drops it (auto-fix).

    Not really sure you need such if there at all, but possible solution would be to separate update on next event loop, like

    if firstPass {
        print("First Pass1: ", firstPass)
        DispatchQueue.main.async {
           firstPass = false
           print("First Pass2: ", firstPass)
        }
    } else {
    

    Tested with Xcode 13 / iOS 15