Trying to use new Swift @Observable to monitor GPS position within SwiftUI content view. But how do I tie the latest locations to the SwiftUI Map's mapCameraPosition?
Well ideally the answer could cover:
Refer to error I'm embedded in the code below.
import SwiftUI
import MapKit
@Observable
final class NewLocationManager : NSObject, CLLocationManagerDelegate {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func startCurrentLocationUpdates() async throws {
if locationManager.authorizationStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
self.location = location
}
}
}
struct ContentView: View {
var newlocationManager = NewLocationManager()
@State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion(
center: newlocationManager.location?.coordinate ?? <#default value#>,
span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25)
))
// GET ERROR: Cannot use instance member 'newlocationManager' within property initializer; property initializers run before 'self' is available
var body: some View {
ZStack {
Map(position: $cameraPosition)
Text("New location manager: \(newlocationManager.location?.description ?? "NIL" )") // works
}
.task {
try? await newlocationManager.startCurrentLocationUpdates()
}
}
}
#Preview {
ContentView()
}
As the error says, you cannot make use of newlocationManager
before the initialization of the view is complete.
Try using .onAppear{...}
as shown in the example code.
struct ContentView: View {
@State var newlocationManager = NewLocationManager() // <--- here
@State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 35.68, longitude: 139.75), // <--- here, adjust
span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25)
))
var body: some View {
ZStack {
Map(position: $cameraPosition)
Text("New location manager: \(newlocationManager.location?.description ?? "NIL" )") // works
}
.task {
try? await newlocationManager.startCurrentLocationUpdates()
}
.onAppear { // <--- here
cameraPosition = .region(MKCoordinateRegion(
center: newlocationManager.location?.coordinate
?? CLLocationCoordinate2D(latitude: 35.68, longitude: 139.75),
span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25)
))
}
}
}
EDIT-3:
@Observable
final class NewLocationManager: NSObject, CLLocationManagerDelegate {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func startCurrentLocationUpdates() async throws {
if locationManager.authorizationStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
DispatchQueue.main.async { // <--- here
self.location = location
}
}
}
}
struct ContentView: View {
@State var newlocationManager = NewLocationManager()
@State var cameraPosition: MapCameraPosition = .automatic
var body: some View {
ZStack {
Map(position: $cameraPosition)
Text("\(newlocationManager.location?.description ?? "NIL" )")
.foregroundStyle(.red)
}
.task {
try? await newlocationManager.startCurrentLocationUpdates()
}
// --- here
.onReceive(newlocationManager.location.publisher) { location in
cameraPosition = .region(MKCoordinateRegion(
center: location.coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.0025, longitudeDelta: 0.0025) // <--- here
))
}
}
}