Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS swift backgound location update when killed/suspended with minimum data loss

I've several questions to ask regarding the location updates in background in swift language.

Let me explain what I'm doing in the app. I'm developing an app which regularly monitors users location (as all of you do) and updates it to the server, so the users movement is tracked and saved for future reference by the user.

Questions

  1. What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?

    1.1 If we use startUpdatingLocation does it impact on publishing the app to the App Store?

  2. When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this?

    2.1 The difference of time for restarting is around 30 sec to nearly 1 min, which doesn't triggers the location update and hence the route is not perfect as shown in the image

Output of the App where due to restart the locations are not recieved and hence the route goes over the road.

Application output

Code for reference

import UIKit
import GoogleMaps

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

    var window: UIWindow?
    let DBName = "test"
    var logFile: FileUtils?
    var viewController:ViewController?

    var count = 0
    var appOpenCount = 0
    let totalPath = GMSMutablePath()
    var leaveCoordinates = 0
    var previousLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
    var locationManager: CLLocationManager?
    var significatLocationManager : CLLocationManager?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        GMSServices.provideAPIKey("*********************")
        logFile = FileUtils(fileName: "\(DBName).txt")

        logFile!.appendFile("\n\nlaunchOptions : \(launchOptions)")

        let defaults = NSUserDefaults.standardUserDefaults()
        count = defaults.integerForKey("Enabled")
        appOpenCount = defaults.integerForKey("appOpenCount")

        if(UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available){
            logFile!.appendFile("\nYessss")
        } else {
            logFile!.appendFile("\nNooo")
        }

        appOpenCount += 1
        defaults.setValue(appOpenCount, forKey: "appOpenCount")
        defaults.synchronize()

        if count == 0 {
            count += 1
            defaults.setValue(count, forKey: "Enabled")
            defaults.synchronize()
            Util.copyFile(count)
        }

        if let launchOpt = launchOptions{
            if (launchOpt[UIApplicationLaunchOptionsLocationKey] != nil) {
                logFile!.appendFile("\nExecuted on : significatLocationManager")
                self.significatLocationManager = CLLocationManager()
                self.significatLocationManager?.desiredAccuracy = kCLLocationAccuracyBest
                self.significatLocationManager?.delegate = self
                self.significatLocationManager?.requestAlwaysAuthorization()
                if #available(iOS 9.0, *) {
                    self.significatLocationManager!.allowsBackgroundLocationUpdates = true
                }
                self.significatLocationManager?.startUpdatingLocation()
            }else{
                logFile!.appendFile("\nExecuted on : locationManager1")
                self.locationManager = CLLocationManager()
                self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
                self.locationManager?.delegate = self
                self.locationManager?.requestAlwaysAuthorization()
                if #available(iOS 9.0, *) {
                    self.locationManager!.allowsBackgroundLocationUpdates = true
                }
                self.locationManager?.startUpdatingLocation()
            }
        } else {
            logFile!.appendFile("\nExecuted on : locationManager2")
            self.locationManager = CLLocationManager()
            self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
            self.locationManager?.delegate = self
            self.locationManager?.requestAlwaysAuthorization()
            if #available(iOS 9.0, *) {
                self.locationManager!.allowsBackgroundLocationUpdates = true
            }
            self.locationManager?.startUpdatingLocation()
        }
        return true
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let locationArray = locations as NSArray
        let newLocation = locationArray.lastObject as! CLLocation
        let coordinate = newLocation.coordinate

        let tempCoor = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
        let lat = tempCoor.latitude
        let lon = tempCoor.longitude
                insert(lat, lon: lon)
    }

    func applicationWillResignActive(application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

    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 applicationWillTerminate: when the user quits.
        if self.significatLocationManager != nil {
            self.significatLocationManager?.startMonitoringSignificantLocationChanges()
        }else{
            self.locationManager?.startMonitoringSignificantLocationChanges()
        }
        logFile!.appendFile("\napplicationDidEnterBackground")
    }

    func applicationWillEnterForeground(application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

    func insert(lat: Double, lon: Double){
        let locationInfo: LocationDetails = LocationDetails()
        locationInfo.latitude = lat
        locationInfo.longitude = lon
        locationInfo.time = NSDate()
        let db = "\(DBName)\(count).sqlite"
        ModelManager.getInstance(db).addLocationData(locationInfo)
    }
}
like image 778
S A R Avatar asked Aug 19 '16 10:08

S A R


2 Answers

question-1

What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?

startUpdatingLocation updates the location when it is called first time and then when the distance filter value exceeds.

it uses the GPS when its available, if you use in continuously it Drain your power/battery

Discussion

This method returns immediately. Calling this method causes the location manager to obtain an initial location fix (which may take several seconds) and notify your delegate by calling its locationManager:didUpdateLocations: method. After that, the receiver generates update events primarily when the value in the distanceFilter property is exceeded. Updates may be delivered in other situations though. For example, the receiver may send another notification if the hardware gathers a more accurate location reading.

Calling this method several times in succession does not automatically result in new events being generated. Calling stopUpdatingLocation in between, however, does cause a new initial event to be sent the next time you call this method.

If you start this service and your application is suspended, the system stops the delivery of events until your application starts running again (either in the foreground or background). If your application is terminated, the delivery of new location events stops altogether. Therefore, if your application needs to receive location events while in the background, it must include the UIBackgroundModes key (with the location value) in its Info.plist file.

In addition to your delegate object implementing the locationManager:didUpdateLocations: method, it should also implement the locationManager:didFailWithError: method to respond to potential errors.

startMonitoringSignificantLocationChanges when a significant change in position occurs.

it uses cellular or wifi, when it work the location is changed or its called in the particular Time interval.

Discussion

This method initiates the delivery of location events asynchronously, returning shortly after you call it. Location events are delivered to your delegate’s locationManager:didUpdateLocations: method. The first event to be delivered is usually the most recently cached location event (if any) but may be a newer event in some circumstances.

Obtaining a current location fix may take several additional seconds, so be sure to check the timestamps on the location events in your delegate method.

After returning a current location fix, the receiver generates update events only when a significant change in the user’s location is detected. For example, it might generate a new event when the device becomes associated with a different cell tower. It does not rely on the value in the distanceFilter property to generate events. Calling this method several times in succession does not automatically result in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does cause a new initial event to be sent the next time you call this method.

If you start this service and your application is subsequently terminated, the system automatically relaunches the application into the background if a new event arrives. In such a case, the options dictionary passed to the locationManager:didUpdateLocations: method of your application delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your application was launched because of a location event. Upon relaunch, you must still configure a location manager object and call this method to continue receiving location events. When you restart location services, the current event is delivered to your delegate immediately. In addition, the location property of your location manager object is populated with the most recent location object even before you start location services.

Note:

Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes. If the device is able to retrieve data from the network, the location manager is much more likely to deliver notifications in a timely manner.

for differenciate purpose I taken from here

question-2

If we use startUpdatingLocation does it impact on publishing the app to the App Store?

One of the possible reasons for 2.16 rejection is the absence of GPS battery warning in your app description on the app meta in iTunesConnect - "The continued use of GPS may decrease battery life" or something like that.

for More information related to this

Question-3

When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this

NO, we Can't Over Come, reason the memory newly Initiated.

like image 78
Anbu.Karthik Avatar answered Nov 15 '22 22:11

Anbu.Karthik


  1. startMonitoringSignificantLocationChanges

    • uses cellular/wifi
    • not accurate but very energy efficient
    • iOS wakes up your app at least every 15 mins or so to give location update (https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html)
  2. startUpdatingLocation

    • uses GPS when available
    • very accurate but eats so much power

Usually, you can submit app using startUpdatingLocation without any problem as long as you state 'Continued use of GPS running in the background can dramatically decrease battery life.' on the app descriptions. And if you put background mode as 'location', use it for location only and not to do anything else.

You don't seem to make use of beginBackgroundTaskWithExpirationHandler, I suggest you use that when your app is in the background.

Obviously, if user does not want your app to get location at all, they can turn off backgroundFetch from the device settings, and touch luck in that case.

like image 34
chuthan20 Avatar answered Nov 15 '22 22:11

chuthan20