I know there are a ton of similar threads, however after three weeks I cannot get this code working. Perhaps Stack Overflow can see an obvious error.
Source of truth comes from this ObservableObject class where a locationManager function updates the @Published variable according to the current geoFence.
class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate, MKMapViewDelegate {
@Published var sceneAccordingToMap = 0
func var1toggle() {
sceneAccordingToMap = 1
}
func var2toggle() {
sceneAccordingToMap = 2
}
func var3toggle() {
sceneAccordingToMap = 3
}
// Bunch of Irrelevant GeoFencing Code
*
*
*
// enter safe area (region)
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered: \(region.identifier) ")
if (region.identifier == "SafeArea"){
print(sceneAccordingToMap)
var1toggle()
print(sceneAccordingToMap)
}
else if (region.identifier == "SafeArea2"){
print(sceneAccordingToMap)
var2toggle()
print(sceneAccordingToMap)
}
}
// exiting safe area (region)
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Left: \(region.identifier) ")
if (region.identifier == "SafeArea"){
print(sceneAccordingToMap)
var1toggle()
print(sceneAccordingToMap)
}
else if (region.identifier == "SafeArea2"){
print(sceneAccordingToMap)
var2toggle()
print(sceneAccordingToMap)
}
}
}
Now when passing this @Published variable to a View containing a @StateObject variable, the data is initially passed, however it fails to update when changed by the functions the MapViewModel. The variables are changing, just not in the View
struct ContentView: View {
@StateObject var dataFromMap = MapViewModel()
var body: some View {
VStack {
MapView().ignoresSafeArea(.all)
Text("\(dataFromMap.sceneAccordingToMap)")
.padding()
}
.environmentObject(MapViewModel())
// Changed to
.environmentObject(dataFromMap)
}
}
Full code of MapView Model:
import CoreLocation
import MapKit
import SwiftUI
import RealityKit
import Combine
// map defaults
enum mapDefaults {
static let initialLocation = CLLocationCoordinate2D(latitude: -37.81785423438109, longitude: 144.97973738630145)
static let initialSpan = MKCoordinateSpan(latitudeDelta:0.01, longitudeDelta:0.01)
}
class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate, MKMapViewDelegate {
@Published var sceneAccordingToMap = 0
func var1toggle() {
sceneAccordingToMap = 1
}
func var2toggle() {
sceneAccordingToMap = 2
}
func var3toggle() {
sceneAccordingToMap = 3
}
@Published private(set) var authorizationStatus: UNAuthorizationStatus?
@Published var region: MKCoordinateRegion = MKCoordinateRegion(
center: mapDefaults.initialLocation,
span: mapDefaults.initialSpan)
var userLatitude = CLLocationCoordinate2D().latitude
var userLongitude = CLLocationCoordinate2D().longitude
var initialCoordinate : CLLocationCoordinate2D?
var movedCoordinate : CLLocationCoordinate2D?
@Published var distanceInMeter : Double = 0.0
let locations = [
Location(name: "Safearea", coordinate: CLLocationCoordinate2D(latitude: 37.331702, longitude: -122.030785)),
Location(name: "SafeArea2", coordinate: CLLocationCoordinate2D(latitude: 37.330709, longitude: -122.030459))
// Create Geofence Region with 10 meter radius
let geofenceRegion = CLCircularRegion(center:CLLocationCoordinate2D(latitude: 37.331702, longitude: -122.030785),
radius: 15,
identifier: "SafeArea")
let geofenceRegion2 = CLCircularRegion(center: CLLocationCoordinate2D(latitude: 37.330709, longitude: -122.030459),
radius: 15,
identifier: "SafeArea2")
var circle = MKCircle(center: mapDefaults.initialLocation, radius: 10)
var locationMangager: CLLocationManager?
// check if location permission is enabled or not
func checkLocationEnabled() {
if CLLocationManager.locationServicesEnabled() {
locationMangager = CLLocationManager()
locationMangager!.delegate = self
locationMangager?.startUpdatingLocation()
locationMangager?.startMonitoring(for: geofenceRegion)
locationMangager?.startMonitoring(for: geofenceRegion2)
print("Location permission enabled")
}
else{
print("Location permission not enabled")
}
}
// Check if user authorized location
private func checkLocationAuth() {
guard let locationMangager = locationMangager else {
return
}
switch locationMangager.authorizationStatus{
case .notDetermined:
locationMangager.requestWhenInUseAuthorization()
case .restricted:
print("User location access is restricted")
case .denied:
print("User denied location access")
case .authorizedAlways, .authorizedWhenInUse:
region = MKCoordinateRegion(center: locationMangager.location!.coordinate, span: mapDefaults.initialSpan)
@unknown default:
break
}
}
// Check if user changed location after first time
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
//check for location authorization
checkLocationAuth()
// request for notification access
requestAuthorization()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
userLatitude = location!.coordinate.latitude
userLongitude = location!.coordinate.longitude
// Starting position
initialCoordinate = CLLocationCoordinate2D(latitude: userLatitude , longitude: userLongitude)
// Updated position
movedCoordinate = CLLocationCoordinate2D(latitude: location!.coordinate.latitude , longitude: location!.coordinate.longitude)
self.distanceInMeter = Double(movedCoordinate!.distance(to: initialCoordinate!))
print(distanceInMeter)
}
// enter safe area (region)
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered: \(region.identifier) ")
if (region.identifier == "SafeArea"){
print("we in pew pew pew pew")
print(sceneAccordingToMap)
var1toggle()
print(sceneAccordingToMap)
}
else if (region.identifier == "SafeArea2"){
print("we in doof doof doof")
print(sceneAccordingToMap)
var2toggle()
print(sceneAccordingToMap)
}
}
// exiting safe area (region)
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Left: \(region.identifier) ")
if (region.identifier == "SafeArea"){
print("we outta pew pew pew pew")
print(sceneAccordingToMap)
var1toggle()
print(sceneAccordingToMap)
}
else if (region.identifier == "SafeArea2"){
print("we outta doof doof doof")
print(sceneAccordingToMap)
var2toggle()
print(sceneAccordingToMap)
}
}
// request for notification
func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { isGranted, _ in
DispatchQueue.main.async {
self.authorizationStatus = isGranted ? .authorized : .denied
}
}
}
// create user notification
func triggerLocalNotification(subTitle: String, body: String){
// configure notification content
let content = UNMutableNotificationContent()
content.title = "Alert!"
content.subtitle = subTitle
content.body = body
content.sound = UNNotificationSound.default
// setup trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// create request
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
// add notification request
UNUserNotificationCenter.current().add(request)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let tileOverlay = overlay as? MKTileOverlay {
let renderer = MKTileOverlayRenderer(overlay: tileOverlay)
return renderer
}
return MKOverlayRenderer()
}
}
// return distance in meters
extension CLLocationCoordinate2D {
/// Returns the distance between two coordinates in meters.
func distance(to: CLLocationCoordinate2D) -> CLLocationDistance {
MKMapPoint(self).distance(to: MKMapPoint(to))
}
}