UNLocationNotificationTrigger working in simulator, but not on physical device

459 Views Asked by At

I have subscribed to a location based reminder with the following code:

let center = CLLocationCoordinate2D(latitude: tapp.location.latitude, longitude: tapp.location.longitude)
let region = CLCircularRegion(center: center, radius: CLLocationDistance(tapp.reminder), identifier: tapp.name)
locationManager.distanceFilter = 100
locationManager.startMonitoring(for: region)
region.notifyOnEntry = true
region.notifyOnExit = false
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
let request = UNNotificationRequest(identifier: "\(tapp.location.latitude)|\(tapp.location.longitude)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

The code works to remind me when I've entered a region in the iOS simulator, but when I load the app onto a physical device and walk into the region, it never notifies me.

I remember reading something about low power mode affecting whether or not you could subscribe to location based notifications, is there anything else like that which stops the app from receiving background updates? Also, is my code correct?

1

There are 1 best solutions below

0
razvan On

I am in a similar situation. Location notifications trigger on simulator, tried in foreground, background and closed app state. It triggers in all cases on simulator, but on real device it does not trigger at all in any case, even foreground.

Here is my code for location permission request

lazy var locationManager: CLLocationManager = createLocationManager()
    
    private func createLocationManager() -> CLLocationManager {
      let manager = CLLocationManager()
      manager.allowsBackgroundLocationUpdates = true
      
      return manager
    }

init() {
        super.init()

        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startMonitoringSignificantLocationChanges()
        locationManager.startUpdatingLocation()
        locationManager.allowsBackgroundLocationUpdates = true
}

func askForPermissionIfNeeded() {
        if CLLocationManager.locationServicesEnabled() {
            if !self.isLocationPermissionAsked {
                locationManager.requestAlwaysAuthorization()
                locationManager.startUpdatingLocation()
            }
        }
    }

And here is my code to request notification permissions

func requestNotificationAuthorization(completion: @escaping () -> Void) {
        let options: UNAuthorizationOptions = [.sound, .alert, .badge]
        notificationCenter
            .requestAuthorization(options: options) { result, _ in
                print("Notification Auth Request result: \(result)")
                completion()
            }
    }

And here is my code to register a location notification

private func registerNotification(region: CLRegion) {
        let notificationContent = UNMutableNotificationContent()
        notificationContent.title = "Active location nearby"
        notificationContent.body = "Do you want to bla bla bla?"
        notificationContent.sound = .default
        
        let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
        
        let request = UNNotificationRequest(
            identifier: region.identifier,
            content: notificationContent,
            trigger: trigger)
        
        notificationCenter
            .add(request) { error in
                if error != nil {
                    print("Error: \(String(describing: error))")
                }
            }
    }

All this works on simulator and doesn't on device...or at least no notification is triggered

BUT I found a workaround that at least in my case works -> Region monitoring

I request always location permission and monitor what regions with locationManager like this

locationManager.startMonitoring(for: fenceRegion)

And with the use of the delegate method didEnterRegion I register a notification there and replace the trigger with a time trigger instead of a location trigger

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        if region is CLCircularRegion {
            registerNotification(region: region)
// Replace let timeTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) in registerNotification
        }
    }

I understand that this is not necessarily a good solution for all, but at least in my case it works. Hopefully it will help others as well