6

I want to get new location every 1min when my app is in background or killed. Here is my code :

Location Service:

class LocationService: NSObject, CLLocationManagerDelegate {

    static let shared = LocationService()
    var locationManager = CLLocationManager()
    var significatLocationManager = CLLocationManager()
    var currentLocation: CLLocation!
    var timer = Timer()
    var bgTask = UIBackgroundTaskInvalid

    func startUpdatingLocation() {
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = kCLDistanceFilterNone
        if #available(iOS 9.0, *) {
            locationManager.allowsBackgroundLocationUpdates = true
        }
        locationManager.delegate = self
        locationManager.startUpdatingLocation()
    }

    func stopUpdatingLocation() {
        locationManager.stopUpdatingLocation()
        timer.invalidate()
    }

    func restartUpdatingLocation() {
        locationManager.stopMonitoringSignificantLocationChanges()
        timer.invalidate()
        startUpdatingLocation()
    }

    func startMonitoringLocation() {
        locationManager.stopUpdatingLocation()
        locationManager.startMonitoringSignificantLocationChanges()
        timer.invalidate()
    }

    func changeLocationAccuracy(){
        switch locationManager.desiredAccuracy {
        case kCLLocationAccuracyBest:
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
            locationManager.distanceFilter = 99999
        case kCLLocationAccuracyThreeKilometers:
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.distanceFilter = kCLDistanceFilterNone
        default: break
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
            UIApplication.shared.endBackgroundTask(self.bgTask)
            self.bgTask = UIBackgroundTaskInvalid
        })

        currentLocation = locations.last!

        let interval = currentLocation.timestamp.timeIntervalSinceNow
        let accuracy = self.locationManager.desiredAccuracy

        if abs(interval) < 60 && accuracy != kCLLocationAccuracyThreeKilometers {        
            timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(LocationService.changeLocationAccuracy), userInfo: nil, repeats: false)
            changeLocationAccuracy()
        }
    } 
}

App delegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     if launchOptions?[UIApplicationLaunchOptionsKey.location] != nil {
          LocationService.shared.restartUpdatingLocation()
     }
}

func applicationDidEnterBackground(_ application: UIApplication) {
     LocationService.shared.restartUpdatingLocation()
}

func applicationWillTerminate(_ application: UIApplication) {
     LocationService.shared.startMonitoringLocation()
}

View controller:

override func viewDidLoad() {
    LocationService.shared.startUpdatingLocation()
}

But my code is not working, i'm not getting new location after a few minutes when my app is in background. and same when my app is suspended or killed. Is there any solution for that ? please not that i'm using Swift 3 & xcode 8.1.

Thank's in advance

YouSS
  • 560
  • 7
  • 20

1 Answers1

4
  • If you call startUpdatingLocation when your app is already in the background, then you will only receive location updates for a few minutes.

  • If you call startUpdatingLocation while your app is in the foreground and then your app moves to the background, you will continue to receive location updates indefinitely.

  • If you call startMonitoringSignificantLocationChanges in the foreground and leave it enabled then you can call startUpdatingLocation in the background and receive updates indefinitely.

  • If the user terminates your app (by swiping up in the app switcher) then you will no longer receive location updates until the user re-launches your app.

Since your code stops and starts location monitoring when it moves to the background of only get location updates for a few minutes. If you start location monitoring foreground and do nothing when the app moves to the background then you will continue to receive location updates.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Can you tell me please what should i change in my code above ? – YouSS Jan 07 '17 at 21:00
  • You need to add a call to `startMonitoringSignificantLocationChanges` where you create your location manager and make sure that this is called from the foreground. You can also remove the code from `applicationWillTerminate` as that serves no purpose. – Paulw11 Jan 07 '17 at 21:03
  • But SignificantLocation give new location only if device move more than 500m. i don't want that i need location every 1min – YouSS Jan 07 '17 at 21:07
  • You can't get it every minute, you will get an update when the location changes, this may be quicker than every minute if the device is moving and less than one minute if it is stationary. The trick is, however, that if significant location monitoring is enabled in the foreground and remains enabled then you can start/stop more accurate location monitoring from the background without the time limits. You can also simply start accurate location monitoring in the foreground and leave it running, ut it uses more battery. Your problem is you *restart* monitoring when you move to the background – Paulw11 Jan 07 '17 at 21:11
  • 1
    You already have the right code. Just don't call `stopUpdatingLocation` when you are in the background – Paulw11 Jan 07 '17 at 21:52