How to keep Core Location and Core Bluetooth running when app is not running (killed/ terminated)?

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 ?

Upvotes: 0

Views: 437

Answers (1)

davidgyoung
davidgyoung

Reputation: 64941

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.

Upvotes: 2

Related Questions