4

I'm currently working on an app and am encountering a problem. The purpose of my app is to see when someone leaves work. For that to work 100% of the time the app should be able to get the user's location while the app is open, in the background or terminated (killed). My understanding is that you have to use the significant location change functionality for this. However, it's not working as expected.

AppDelegate:

    //handle location changes when app is terminated and waken up by the OS
    if launchOptions?[UIApplicationLaunchOptionsKey.location] != nil {
        print("Restarted app due to a location update")
        request = Locator.subscribeSignificantLocations(onUpdate: { newLoc in
            let db: FirebaseDB = FirebaseDB.shared
            db.postCoordinates(coordinates: CLLocation(latitude: 9.99, longitude: 9.99))
            print("new loc found -> \(newLoc)")
        }, onFail: {(err, lastLoc) in
            print("failed to get loc, -> \(err)")
        })
        //send new post to DB
    }

    //subscribeSignificantLocations -> power efficient
        request = Locator.subscribeSignificantLocations(onUpdate: { newLoc in
            print("new loc found -> \(newLoc)")
        }, onFail: {(err, lastLoc) in
            print("failed to get new loc, -> \(err)")
        })

Everything works except for getting the location when the app is terminated...

Thanks in advance!

Mathias Schrooten
  • 722
  • 2
  • 11
  • 20
  • try this https://stackoverflow.com/questions/37450524/ios-getting-location-updates-when-app-terminated-without-using-significantchange – Ashish Nov 30 '17 at 09:03
  • tru this http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended – Balaji Kondalrayal Nov 30 '17 at 09:24
  • @Ashish I implemented the significant location change service as well as the region monitoring services (geofences as seen in raywenderlich.com/136165/core-location-geofencing-tutorial) – Mathias Schrooten Nov 30 '17 at 09:28
  • @BalajiKondalrayal Implemented that as well as you can see in my AppDelegate code – Mathias Schrooten Nov 30 '17 at 09:29
  • @MathiasSchrooten, you can refer my stack overflow answer also it might be helpful https://stackoverflow.com/questions/47539720/ios11-location-update-after-getting-killed-by-user/47546956#47546956 – Ashish Nov 30 '17 at 09:43
  • @Ashish, thanks for your answer, already got that working though :( – Mathias Schrooten Nov 30 '17 at 10:17
  • @MathiasSchrooten I have noticed this behaviour in an uber like app that I built, where I tracked the user's location even when the app was terminated. I noticed that around 70% of the times the location callback would kick in after the user moved significantly (tested on ios 9, ios10), factors like battery, current open apps and network play a role in the callback. Can you please try with a full charge and all other apps killed and report the results ? – B K Nov 30 '17 at 15:57
  • @MathiasSchrooten did you find any solution? – Amanpreet May 24 '18 at 13:35

1 Answers1

7

After hours of searching and reading for the solution, I found a working solution for me. (NOTE: I am working on geofencing where I need to notify/call API the user when he leaves certain area)

The first and most important step is to have "always" authorization by the user for location access.

Second, we need to use startMonitoringSignificantLocationChanges() for location update even after the app termination.

        locationManager.requestAlwaysAuthorization()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
        locationManager.startMonitoringSignificantLocationChanges() //THIS IS WHERE THE MAGIC HAPPENS

Below is the code for getting the local notifications for location update.

import UIKit


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

    var window: UIWindow?
    let locationManager = CLLocationManager()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.


        locationManager.requestAlwaysAuthorization()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
        locationManager.startMonitoringSignificantLocationChanges()

        application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil))
        UIApplication.shared.cancelAllLocalNotifications()

        return true
    }

    func alertUserOnLeaving(region:CLRegion){
        if UIApplication.shared.applicationState == .active {
            let alert = UIAlertController(title: "Alert Title", message: "Alert Message", style = .Alert
            window?.rootViewController?.present(alert, animated: true, completion: nil)
        }
        else{
            let notification = UILocalNotification()
            notification.alertBody = "You forgot to checkout"
            notification.soundName = "Default"
            UIApplication.shared.presentLocalNotificationNow(notification)
        }
    }

    func alertUserOnArrival(region:CLRegion){
        if UIApplication.shared.applicationState == .active {
            let alert = UIAlertController(title: "Alert Title", message: "Alert Message", style = .Alert
            window?.rootViewController?.present(alert, animated: true, completion: nil)
        }
        else{
            let notification = UILocalNotification()
            notification.alertBody = "Welcome Please checkin"
            notification.soundName = "Default"
            UIApplication.shared.presentLocalNotificationNow(notification)
        }
    }

    func setUpGeofenceForJob() {
        let geofenceRegionCenter = CLLocationCoordinate2DMake(-33.7513580322265, 151.242416381836)
        let geofenceRegion = CLCircularRegion(center: geofenceRegionCenter, radius: 100, identifier: "GeoFence")
        geofenceRegion.notifyOnExit = true
        geofenceRegion.notifyOnEntry = true
        self.locationManager.startMonitoring(for: geofenceRegion)
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if (status == CLAuthorizationStatus.authorizedAlways) {
            self.setUpGeofenceForJob()
        }
    }

    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        alertUserOnArrival(region: region)
    }
    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        alertUserOnLeaving(region: region)
    }

Hope it helps.

Hiren
  • 676
  • 7
  • 21
  • 2
    I want location updated every 15 minutes when application is killed/terminated/suspended state. Is it possible to get? – Rashesh Bosamiya Feb 27 '20 at 11:09
  • I want the location updated every 15 minutes when the application is killed/terminated/suspended state. Is it possible to get it? Please suggest. – Piyush May 30 '22 at 05:23