Search code examples
iosswiftbackgroundcore-locationcllocationmanager

Reason for my app going to suspended state?


I am created a location tracking ios app(using CocoaLumberjack library to write log file).So background location update is enabled and working for my testing(I nearly running of upto 8 hours in background). When app goes to live store. There is lot of issue occurred in our app. When app went to background location tracking is not working properly. It's not send user location to server for some period of time. So i get log file from client and reviewed there is a time gap in log file. i frequently getting user location(every one second). So i thought app went to suspended state at the time of gap occurs in log file? Why app goes into suspended state even i am getting frequently location in background? is there a reason for app going to suspended state? searched lot can't find any valid details?

 func startTimer()
{
    if bgTimer == nil
    {
        bgTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.startLocationChanges), userInfo: nil, repeats: true)
    }
}

func stopTimer()
{
    if bgTimer != nil
    {
        bgTimer?.invalidate()
        bgTimer = nil
    }
}

@objc func startLocationChanges() {
    locationManager.delegate = self
    locationManager.allowsBackgroundLocationUpdates = true
    locationManager.pausesLocationUpdatesAutomatically = false
    locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
    locationManager.requestAlwaysAuthorization()
    locationManager.startUpdatingLocation()
}

func locationManager(_ manager: CLLocationManager,  didUpdateLocations locations: [CLLocation]) {
    //let lastLocation = locations.last!

    // Do something with the location.
    /*print(lastLocation)
    let logInfo = "BGLocationManager didUpdateLocations : " + "\(lastLocation)"
    AppDelegate.appDelegate().writeLoggerStatement(strInfo: logInfo)*/

    locationManager.stopUpdatingLocation()
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {

    if let error = error as? CLError, error.code == .denied {
        // Location updates are not authorized.
        manager.stopMonitoringSignificantLocationChanges()
        return
    }

    // Notify the user of any errors.
}



func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationDidEnterBackground: when the user quits.
    self.writeLoggerStatement(strInfo: "applicationDidEnterBackground")
    appstate = "Background"

    if CoreDataUtils.isUserLoggedIn(entityName: "UserInfo") == true {
        let user = CoreDataUtils.fetchCurrentUser(entityName: "UserInfo")
        if user!.isGPSActive == "1"
        {
            if backgroundTaskIdentifier != nil
            {
                application.endBackgroundTask(backgroundTaskIdentifier!)
                backgroundTaskIdentifier = UIBackgroundTaskInvalid
            }

            backgroundTaskIdentifier = application.beginBackgroundTask(expirationHandler: {
                //UIApplication.shared.endBackgroundTask(self.backgroundTaskIdentifier!)
            })

            BGLocationManager.shared.startTimer()

            let logInfo = String(format:"applicationDidEnterBackground backgroundTimeRemaining : %f",(Double)(application.backgroundTimeRemaining / 60))
            self.writeLoggerStatement(strInfo: logInfo)
        }
    }
}

Solution

  • A few observations:

    1. The beginBackgroundTask only buys you 30 seconds, not 8 hours. (In iOS versions prior to 13, this was 3 minutes, not 30 seconds, but the point still stands.) Bottom line, this is designed to allow you to finish some short, finite length task, not keeping the app running indefinitely. Worse, if you don’t call endBackgroundTask in its completion handler, the app will be unceremoniously terminated when the allotted time has expired.

    2. There are two basic patterns to background location updates.

      • If the app is a navigation app, then you can keep the app running in the background. But keeping standard location services running in the background will kill the user’s battery in a matter of a few hours. So Apple will only authorize this if your app absolutely requires it (e.g. your app is an actual navigation app, not just an app that happens to want to keep track of locations for some other reason).

      • The other pattern is significant change service. With this service, your app will be suspended, but the OS will wake it to deliver location updates, and then let it be suspended again. See Handling Location Events in the Background. This isn’t as precise as the standard location services, but because the app isn’t constantly running and because it doesn’t have to spin up GPS hardware, it consumes far less power.

    3. When testing these sorts of background interactions, you do not want to be attached to the Xcode debugger. Running it via the debugger actually changes the app lifecycle, preventing it from ever suspending.

    4. As one doesn’t generally keep the app running in the background indefinitely, that means that you will want to remove that Timer related code.