1

I want to use MapKit along with MapAnnotation in SwiftUI. So I can have custom views as map pins and add tap gestures to them later.

But when I run the app, Xcode starts to show so many UI errors:

Publishing changes from within view updates is not allowed, this will cause undefined behavior.

The Code is as simple as we can find, even in apple documentation or the ones below from hacking with swift

    import SwiftUI
    import MapKit

    struct Location: Identifiable {
        let id = UUID()
        let name: String
        let coordinate: CLLocationCoordinate2D
    }

    struct MapView: View {

        @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.5, longitude: -0.12), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
        
        let locations = [
            Location(name: "Buckingham Palace", coordinate: CLLocationCoordinate2D(latitude: 51.501, longitude: -0.141)),
            Location(name: "Tower of London", coordinate: CLLocationCoordinate2D(latitude: 51.508, longitude: -0.076))
        ]
        
        var body: some View {
            Map(coordinateRegion: $region, annotationItems: locations) { location in
                MapAnnotation(coordinate: location.coordinate) {
                    Circle()
                        .stroke(.red, lineWidth: 3)
                        .frame(width: 44, height: 44)
                }
            }
            .navigationTitle("Map")
            .edgesIgnoringSafeArea(.all)
        }
    }

Would you happen to have any suggestions or workaround?

Can we use MapKit with these huge errors in a Production Release?


UPDATE

Based on recent answer, As a workaround, using a specific Binding<MKCoordinateRegion>() for the coordinateRegion, removes the warning/error.

But if you want to move the map based on the selected pin, those errors/warnings will appear again.

struct MapView: View {

    @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.5, longitude: -0.12), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
    
    let locations = [
        Location(name: "Buckingham Palace", coordinate: CLLocationCoordinate2D(latitude: 51.501, longitude: -0.141)),
        Location(name: "Tower of London", coordinate: CLLocationCoordinate2D(latitude: 51.508, longitude: -0.076))
    ]
    
    @State var selectedLocation: Location?

    var body: some View {
        ZStack {
            Map(coordinateRegion: Binding<MKCoordinateRegion>(
                get: { region },
                set: { _ in }
            ), annotationItems: locations) { location in
                MapAnnotation(coordinate: location.coordinate) {
                    Circle()
                        .stroke(.red, lineWidth: 3)
                        .frame(width: 44, height: 44)
                        .onTapGesture {
                            selectedLocation = location
                        }
                }
            }
        }
        .onChange(of: selectedLocation) { newValue in
            if let location = newValue {
                withAnimation {
                    region = MKCoordinateRegion(center: location.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
                }
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
}
Sajjad Sarkoobi
  • 827
  • 8
  • 18
  • it could be a bug, according to this post: https://stackoverflow.com/questions/71953853/swiftui-map-causes-modifying-state-during-view-update – workingdog support Ukraine Jan 03 '23 at 13:18
  • 1
    I don't think it is ready for a production release. I looked at this closely and I believe the design of the MapKit framework just doesn't fit into SwiftUI's state-driven design and I don't think Apple can change it without breaking lots of their apps. E.g. an example is when you set things on the MKMapView, it also calls delegate methods which is not the case in UIKit. – malhal Jan 03 '23 at 16:28

1 Answers1

0

As a workaround, using a specific Binding<MKCoordinateRegion>() for the coordinateRegion, removes the warning/error for me.

struct MapView: View {
    @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.5, longitude: -0.12), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
    
    let locations = [
        Location(name: "Buckingham Palace", coordinate: CLLocationCoordinate2D(latitude: 51.501, longitude: -0.141)),
        Location(name: "Tower of London", coordinate: CLLocationCoordinate2D(latitude: 51.508, longitude: -0.076))
    ]

    var body: some View {
        Map(coordinateRegion: Binding<MKCoordinateRegion>(
            get: { region },
            set: { _ in }
        ), annotationItems: locations) { location in
            MapAnnotation(coordinate: location.coordinate) {
                Circle().stroke(.red, lineWidth: 3).frame(width: 44, height: 44)
            }
        }
         .navigationTitle("Map")
         .edgesIgnoringSafeArea(.all)
    }
}
dudette
  • 788
  • 3
  • 8
  • Thanks for your answer, it solved the problem, but I ran into another one again. If you put a TapGesture on Circle() to get the location and try to change the region, it shows those UI errors again. – Sajjad Sarkoobi Jan 03 '23 at 16:32