I'm looking for a way that I can track that a user has arrived near a designated set of co-ordinates. The functionality needs to work while the application is in the background (preferably within 100 metres). Also, to preserve the battery, I ideally do not want to get too many co-ordinate readings (perhaps a reading every 10 minutes for no longer than a couple of hours).
There are a couple of ways that I have tried to accomplish this task, but have been unable to obtain the desired result:
Background Timer:
I had added a background task in (App.delegate)
func applicationDidEnterBackground(_ application: UIApplication)
Which executed a repeated Timer.scheduledTimer to get co-ordinates and process
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
to detect if the user was within range. This method worked if applied in the short-term, but only until the application was suspended, which was about 3 minutes. Ideally, I would not want to get co-ordinates this frequently.
Region Monitoring:
I had initialised the CLLocationManager as shown below:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.activityType = .otherNavigation
locationManager.requestAlwaysAuthorization()
}
The LocationManager starts when the application enters into the background:
func applicationDidEnterBackground(_ application: UIApplication) {
self.monitorRegionAtLocation(center: CLLocationCoordinate2D(latitude: x, longitude: y), identifier: id)
locationManager.startUpdatingLocation()
}
Code for monitoring of region:
func monitorRegionAtLocation(center: CLLocationCoordinate2D, identifier: String ) {
// Make sure the app is authorized.
if CLLocationManager.authorizationStatus() == .authorizedAlways {
// Make sure region monitoring is supported.
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// Register the region.
let maxDistance = 200.0
let region = CLCircularRegion(center: center,
radius: maxDistance, identifier: identifier)
region.notifyOnEntry = true
region.notifyOnExit = false
locationManager.startMonitoring(for: region)
}
}
}
And I added a didEnterRegion function block for CLLocationManager:
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if let region = region as? CLCircularRegion {
let identifier = region.identifier
print("FOUND: " + identifier)
}
}
The code appears to work for detecting entry into a region, however the co-ordinates are not updating while in the background.
Additional Information
- I have the Background Modes of Location Updates and Background Fetch enabled
- I have supplied values for 'Location Always Usage Description' and 'Location When in Use Usage Description' in the Info.plist
- The App Settings shows 'Always' permission against the Location
I believe that there has to be a better way of operating these kinds of checks in the background, but I haven't discovered any method of detecting other movements in the background.
Any direction on this matter would be greatly appreciated, and if you need any more information, please let me know and I'll provide what I can.
UPDATE:
I have modified the approach following the advice of comments below to use Region Monitoring.