Публикация изменений из обновлений представления 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 устранит это предупреждение.