0

I got stuck when trying to keep my function running if I terminate the app.

Is it possible to keep core location (geofencing / geolocating) and core bluetooth running when app is not running ? If possible how to solve my issue? I already check the background modes, and implement core location method. this is my code :

    class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

        var viewController = ViewController()
        var window: UIWindow?

        var locationManager = CLLocationManager()

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

            locationManager = CLLocationManager()
            locationManager.requestAlwaysAuthorization()

            locationManager.delegate = self
            locationManager.distanceFilter = kCLDistanceFilterNone
            locationManager.desiredAccuracy = kCLLocationAccuracyBest

            locationManager.pausesLocationUpdatesAutomatically = false

            if #available(iOS 9.0, *) {
                locationManager.allowsBackgroundLocationUpdates = true
            }

            beaconRegion.notifyEntryStateOnDisplay = true
            beaconRegion.notifyOnEntry = true
            beaconRegion.notifyOnExit = true

            locationManager.startMonitoring(for: beaconRegion)
            locationManager.stopRangingBeacons(in: beaconRegion)
            locationManager.startRangingBeacons(in: beaconRegion)
            locationManager.startUpdatingLocation()

            return true
        }

        func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
            if (region.identifier == beaconRegionIdentifier) {
                manager.requestState(for: region)
                goBackground()
            }
        }

        func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
            print("you exited region")
        }

        func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
            if (region.identifier == beaconRegionIdentifier) {
                manager.stopUpdatingLocation()

                switch state {
                case .inside:
                    manager.startRangingBeacons(in: region as! CLBeaconRegion)
                    manager.startUpdatingLocation()
                case .outside:
                    let delay = DispatchTime.now() + .seconds(3)
                    DispatchQueue.main.asyncAfter(deadline: delay) {
                        manager.requestState(for: region)
                    }
                case .unknown:
                    let delay = DispatchTime.now() + .seconds(3)
                    DispatchQueue.main.asyncAfter(deadline: delay) {
                        manager.requestState(for: region)
                    }
                }
            }
        }

        func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {

            let state = UIApplication.shared.applicationState
            switch(state){
            case.active,
                .inactive,
                .background:

                let mainView = UserDefaults.standard.bool(forKey: "toMainView")

                var isWakeUpRunning = UserDefaults.standard.bool(forKey: "isWakeUpRunning")

                if isWakeUpRunning {
                    if mainView {
                        for aBeacon in beacons{
                            if aBeacon.rssi > WAKE_UP_THRESHOLD   && aBeacon.rssi != 0{
                                UserDefaults.standard.set(false, forKey: "isWakeUpRunning")
                                self.viewController.startFunction()
                                manager.stopRangingBeacons(in: region)
                            }else if aBeacon.rssi != 0 && aBeacon.rssi < WAKE_UP_THRESHOLD {
                                manager.stopRangingBeacons(in: region)
                                manager.requestState(for: region)
                            }
                        }
                    }
                }else{
                    manager.stopRangingBeacons(in: region)
                    manager.requestState(for: region)
                }
            }
        }  

 func goBackground() {
        let app = UIApplication.shared
        var bgTask: UIBackgroundTaskIdentifier = 0
        bgTask = app.beginBackgroundTask(expirationHandler: {
            NSLog("Expired: %lu", bgTask)
            app.endBackgroundTask(bgTask)
        })
        NSLog("Background: %lu", bgTask)
}

    }

From my code, it can run in both in foreground and background. but when i swipe up the app from task switcher, it cannot work. and i cannot see the log too from xcode. Any ideas to help me ?

1 Answers1

2

Once you swipe up the app from the task switcher your app will be killed, but CoreLocation will still be working for you at the OS level, looking for beacons that match your region:

locationManager.startMonitoring(for: beaconRegion)

Once there is a change on that region (either a didEnter or a didExit), then your app will be re-launched into the background. At this time the following methods will be called in order:

  1. application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)

  2. locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion)

At that time your app will continue running in the background. Because your app starts a background task, this will continue for 180 seconds before being suspended.

However, your app will not run between the time it is swiped off the screen and when the next region transition happens. This is the best you can do.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • if my app is launched (after it was **terminated**) through exiting a region and inside there I do `locationManager.startUpdatingLocation` while I have locations enabled in background modes...would my location work for 180 or indefinitely? 2. If app was kept alive in **background or suspended**, but for some reason my location Tracking was paused by OS (ie not paused by me), am I able to resume indefinitely location Tracking or again 180 seconds? – mfaani Jul 26 '17 at 20:44
  • (1) Your location would start updating again on region entry/exit, and if you have successfully received permission to run in the background, it will continue running indefinitely, just as if the app was never killed. (2) I am not sure *how* you are suggesting tracking would be paused. If a user turns off location for example, then no, your app will not be able to resume tracking until the user turns location back on. – davidgyoung Jul 26 '17 at 21:02
  • Paused by `locationManagerDidPauseLocationUpdates` callback – mfaani Jul 26 '17 at 21:35
  • According to the docs [here](https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/1621553-locationmanagerdidpauselocationu) that method gets called when the location is not changing. Your app may again call `locationManager.startUpdatingLocation` at an time afterward. I don't think this has anything to do with whether you are in the background or not. – davidgyoung Jul 26 '17 at 22:14