Getting location for an iOS app when it is in the background and even killed
Asked Answered
G

1

7

My app needs to get the user's location when app is active and when it's inactive and killed. When the user's location is near to a store the app has to send a local notification.

I'm not sure what exactly is happening, but I'm not able to make my app get the location in the background and wakes it up when is killed.

I have a location manager (singleton, used for boths cases whenInUse and Always), and I have both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription defined in .plist

What I'm doing is:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //The app has been killed/terminated (not in background) by iOS or the user.
    if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]){

        _locationManager = [CoreLocationManager sharedInstance];
        _locationManager.isAppActive = NO;
        _locationManager.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
        _locationManager.locationManager.activityType = CLActivityTypeOtherNavigation;

        if ([_locationManager.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
            [_locationManager.locationManager requestAlwaysAuthorization];
        }

        [_locationManager addLocationManagerDelegate:self];
    }
}


- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if (_locationManager.locationManager){
        _locationManager.isAppActive = YES;
        [_locationManager.locationManager stopMonitoringSignificantLocationChanges];
    }

    _locationManager = [CoreLocationManager sharedInstance];

    if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        [_locationManager.locationManager requestAlwaysAuthorization];
    }

    if ([_locationManager.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [_locationManager.locationManager requestWhenInUseAuthorization];
    }

    [_locationManager addLocationManagerDelegate:self];

    [_locationManager.locationManager startUpdatingLocation];

}


- (void)applicationDidEnterBackground:(UIApplication *)application
{
    _locationManager.isAppActive = NO;

    if (_locationManager.locationManager){
        [_locationManager.locationManager stopUpdatingLocation];
        [_locationManager.locationManager stopMonitoringSignificantLocationChanges];
    }

    if ([_locationManager.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        [_locationManager.locationManager requestAlwaysAuthorization];
    }

    [_locationManager.locationManager startMonitoringSignificantLocationChanges];

}

Do I make something wrong? I'm not sure if it's strictly necessary to use geofencing, but for the things I've read with startMonitoringSignificantLocationChanges is enough.

Gabion answered 22/5, 2015 at 12:10 Comment(0)
S
9

To get a location in the background, use the following code. It will make your app run in the background for a long time by restarting the background task everytime.

To use this, you need to turn on Background Mode in Capabilities in project settings with Background Fetch and Location Updates turned on.

- (void)applicationDidEnterBackground:(UIApplication *)application {

    if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) { //Check if our iOS version supports multitasking I.E iOS 4

        if ([[UIDevice currentDevice] isMultitaskingSupported]) { //Check if device supports mulitasking
            UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance

            __block UIBackgroundTaskIdentifier background_task; //Create a task object

            background_task = [application beginBackgroundTaskWithExpirationHandler: ^{
                [application endBackgroundTask:background_task]; //Tell the system that we are done with the tasks
                background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
                //System will be shutting down the app at any point in time now
            }];
        }
    }
}
Sensitize answered 22/5, 2015 at 12:12 Comment(10)
Thanks Utsav!! Where I have to insert the locationmanager code of the applicationDidEnterBackground?Gabion
@Gabion Your code in didEnterBackground will not be needed. You never need to call stopUpdatingLocation as you need continuous location updates. If you call stop UpdatingLocation the user will stop getting location updates. You should do that only whenever user logs out of the appSensitize
Sorry if I ask a stupid thing @utsav, but Is it not necessary to call stopUpdatingLocation (because is used when app is running) and then call startMonitoringSignificantLocationChanges?Gabion
@Gabion startUpdatingLocation and startMonitoringSignificantLocationChanges are not meant to be used concurrently. It's either or as they both deliver heading and location changes to the same delegate method. locationManager:didUpdateToLocation:fromLocationSensitize
Ok @utsav, I know that, but what about the others AppDelegate methods? Do I have to reallocate the location manager as I do in application: didFinishLaunchingWithOptions? And in applicationDidBecomeActive? Because If I'm using the normal methods startUpdatingLocation and stopUpdating location when the app is in use, I think I have to stop and start as I need to use the startMonitoringSignificantLocationChanges, because this is the only method can wake up the app when location changes. startUpdating location can'tGabion
@Gabion If you have location updates enabled in background, you can use this method - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations. When startUpdating is called, this method will be called even if the app is in background. So as you are saying startMonitoringSignificantLocationChanges is the only method that can wake up the app is not true. When startUpdatingLocation is triggered, this method will be called only if there is significant change in location depending on the meters you set as accuracy(KLocationAcuuracyBest).Sensitize
will didUpdateLocations get called even if the app is killed?Pindus
Does this really work? "restarting the background task everytime" how is that happening?Noise
@Gabion : please let me know whether you are able to get the current location even in app killed state or terminated state ?Arndt
@UtsavParikh : please let me know is it possible to get the current location using this even though app is in killed or terminated state?Arndt

© 2022 - 2024 — McMap. All rights reserved.