Публикация изменений из обновлений представления MAP() не разрешена, это приведет к неопределенному поведению.

Я видел похожие вопросы по этому поводу, но ни один из них не обновлен или не работает успешно, я следил за документами Apples Developer, различными видео и руководствами о том, как это сделать, но он все равно выдает эти предупреждения.

2023-01-11 11:33:16.044044-0500 Walkman[50986:4824621] [SwiftUI] Publishing changes from within view updates is not allowed, this will cause undefined behavior.

Такое ощущение, что я пробовал все, от onChange() до @StateObject вместо @ObservedObject... Это те несколько строк, потому что предупреждения исчезают, если карта закомментирована.

Код, вызывающий ошибку,

      struct OrderView: View {
    @ObservedObject var cartManager: CartManager
//    @ObservedObject var locationManager: LocationManager
    
    @EnvironmentObject var locationManager: LocationManager
    @EnvironmentObject var viewModel: AppViewModel
    @State private var tracking:MapUserTrackingMode = .follow
    @State private var driversIn: Bool = false
    
    var body: some View {
        NavigationView {
//            Text("Testing")
            Map(
                coordinateRegion: $locationManager.region,
                interactionModes: MapInteractionModes.all,
                showsUserLocation: true,
                userTrackingMode: $tracking,
                annotationItems: locationManager.MapLocations,
                annotationContent: { item in
                    MapMarker(coordinate: item.coordinate, tint: .red)
//                    MapAnnotation(coordinate: item.location) {
//                        Rectangle()
//                            .stroke(Color.purple, lineWidth: 3)
//                            .frame(width: 10, height: 10)
//                    }
                }
            )
            .navigationTitle("Orders")

            .onAppear {
                locationManager.getNearbyDrivers(query: GeoQuery(distance: 400, geo: locationManager.createGeo()))
            }
            .ignoresSafeArea()
//            .onReceive(locationManager.$drivers, perform: { drivers in
////                print("DEBUG: DRIVERS _ \(drivers)")
//                for driver in drivers {
//                    print("DEBUG: DRIVER TEST _ \(driver)")
//                    locationManager.MapLocations.append(MapLocation(id: driver.id, name: driver.name, latitude: driver.location.coordinates[0], longitude: driver.location.coordinates[1]))
//                }
//            })
        }
        
        
    }
}

Вот мой LocationManager

      final class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
    @Published var region = MKCoordinateRegion(center: MapDetails.startingLocation, span: MapDetails.defaultSpan)
    @Published var authorizationStatus: CLAuthorizationStatus?
    @Published var location: CLLocation?
    @Published var MapLocations = [MapLocation]()
    
    @Published var drivers = [Driver]()
    var locationManager = CLLocationManager()
    
    override init() {
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedWhenInUse:
            authorizationStatus = .authorizedWhenInUse
            manager.startUpdatingLocation()
            break
        case .restricted, .denied:
            authorizationStatus = .restricted
            manager.stopUpdatingLocation()
            break
        case .notDetermined:
            authorizationStatus = .notDetermined
            manager.requestWhenInUseAuthorization()
            break
        default:
            break
        }
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        DispatchQueue.main.async {
            self.location = location
//            self.MapLocations.append(MapLocation(name: "1", latitude: location.coordinate.latitude, longitude: location.coordinate.longitude))
            // MARK - API for location calls, send when users location updates
            print("DEBUG: Location: \(self.location?.coordinate.longitude ?? 0.00)")
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("DEBUG: \(error.localizedDescription)")
    }
    
    func createGeo() -> GeoJSON {
        return GeoJSON(coordinates: [locationManager.location?.coordinate.latitude ?? 0.00, locationManager.location?.coordinate.longitude ?? 0.00])
    }

    
    func getNearbyDrivers(query: GeoQuery) {
        guard let url = URL(string: "http://localhost:8000/drivers-nearby") else { fatalError("Missing URL") }
        
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        let payload: [String: Any] = [
            "distance": query.distance,
            "geo": ["type": "Point", "coordinates": query.geo.coordinates]
        ]
        let data = try! JSONSerialization.data(withJSONObject: payload, options: [])
        request.httpBody = data

        let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let error = error {
                print("Request error: ", error)
                return
            }
            guard let response = response as? HTTPURLResponse else { return }
            
            if response.statusCode == 200 {
                guard let data = data else { return }
                DispatchQueue.main.async {
                    do {
                        let drivers = try JSONDecoder().decode([Driver].self, from: data)
                        
                        print("DEBUG: Drivers: \(drivers)")
                        self.drivers = drivers
                        for driver in self.drivers {
                            let location = MapLocation(id: driver.id, name: driver.name, latitude: driver.location.coordinates[0], longitude: driver.location.coordinates[1])
                            var driverIsVisible: Bool {
                                return self.MapLocations.contains(where: { annotation in
                                    guard var driverAnno = annotation as? MapLocation else { return false }
                                    print("DEBUG: cast driverAnno | driverAnno \(driverAnno.id) | driver \(driver.id)")
                                    if driverAnno.id == driver.id {
                                        print("DEBUG: Handle update driver position")
                                        driverAnno.latitude = driver.location.coordinates[0]
                                        driverAnno.longitude = driver.location.coordinates[1]
                                        return true
                                    }
                                    return false
                                })
                            }
                            
                            if !driverIsVisible {
                                print("DEBUG: Inserting map annotation \(location.id)")
                                self.MapLocations.append(location)
                            }
                        }
                        
                    } catch let error {
                        print("Error decoding: ", error)
                    
                    }
                }
            }
        }
        
        dataTask.resume()
    }
    

    
////    private func geocode() {
////        guard let location = self.location else { return }
////        geocoder.reverseGeocodeLocation(location) { (places, error) in
////            if error == nil {
////                self.placemark = places?[0]
////            } else {
////                self.placemark = nil
////            }
////        }
////    }
}

2 ответа

У меня была похожая проблема, и все ответы в Интернете, похоже, предполагают, что это ошибка Apple, что может быть правдой, но есть обходной путь.

Патрик выше уже намекнул на виновника, дело в переменной, но ее удаление не является для вас правильным решением. Проблема в том, чтоregion— это переменная, которая также используется какBindingпеременная (т.е.$locationManager.regionв вашем коде). Нет ничего плохого в таком шаблоне кода, но я предполагаю, что платформе Map он не нравится (отсюда и ошибка Apple).

Обходной путь: вам нужен промежуточный шаг в представлении SwiftUI, который будет реагировать на изменения в@Publishedпеременную и привязать ее к пользовательскому интерфейсу, чтобы вы не привязывали опубликованную переменную непосредственно кcoordinateRegion

пример:

      struct ContentView: View {
 @State var mapRegion: MKCoordinateRegion = .init(.null) // this is the intermediate step mentioned above, your Map will bind to this instead of @Published variable
....
.....
......
   Map(...) {
   }
   .onReceive(viewModel.$mapRegion, perform: { region in
        self.mapRegion = region // subscribe to the @published variable
   })
}

Удаление @Published из региона в LocationManager устранит это предупреждение.

Другие вопросы по тегам