6

I want to my app to keep running in the background with location services. For this I have used:

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

    [locationManager stopUpdatingLocation];
    [locationManager startUpdatingLocation];

    //timer=[NSTimer scheduledTimerWithTimeInterval:300 target:self selector:@selector(UpdateLocation) userInfo:nil repeats:YES];

}

but when I use NSTimer it does not call UpdateLocation. I tried calling it using another method but then it also called only once.

I want to run the app continuously in the background, detecting locations after regular interval of time.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Steve
  • 61
  • 1
  • 3

3 Answers3

6

I did this in an application I'm developing. The timers don't work when the app is in the background but the app is constantly receiving the location updates. I read somewhere in the documentation (i can't seem to find it now, i'll post an update when i do) that a method can be called only on an active run loop when the app is in the background. The app delegate has an active run loop even in the bg so you dont need to create your own to make this work. [Im not sure if this is the correct explanation but thats how I understood from what i read]

First of all, add the 'location' object for the key 'Background Modes' in your app's info.plist. Now, what you need to do is start the location updates anywhere in your app:

CLLocationManager locationManager = [[CLLocationManager alloc] init];

locationManager.delegate = self;//or whatever class you have for managing location

[locationManager startUpdatingLocation];

Next, write a method to handle the location updates, say -(void)didUpdateToLocation:(CLLocation*)location, in the app delegate. Then implement the method locationManager:didUpdateLocation:fromLocation of CLLocationManagerDelegate in the class in which you started the location manager (since we set the location manager delegate to 'self'). Inside this method you need to check if the time interval after which you have to handle the location updates has elapsed. You can do this by saving the current time every time. If that time has elapsed, call the method UpdateLocation from your app delegate:

NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@"New Location: %@", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}

This will call your method every 5 mins even when your app is in background. Imp: This implementation drains the battery, if your location data's accuracy is not critical you should use [locationManager startMonitoringSignificantLocationChanges]

Before adding this to your app, please read the Location Awareness Programming Guide at http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/Introduction/Introduction.html

animuson
  • 53,861
  • 28
  • 137
  • 147
Bushra Shahid
  • 3,579
  • 1
  • 27
  • 37
  • If I am not mistaken, this will only call didUpdateToLocation every 5 minutes if didUpdateLocation is called at regular small intervals. If you select startMonitoringSignificantChanges and do not move much, then you will not get updates at a 5 mn interval. Correct? – Lolo Mar 02 '13 at 15:00
0

I know this is very late for an answer. But if you still haven't got the answer, here is what I did and my app continuously runs in the background.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
     UIApplication *app = [UIApplication sharedApplication];
     UIBackgroundTaskIdentifier bgTask = 0;

     backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self  selector:@selector(backgroundTask) userInfo:nil repeats:YES];

     bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
    }];
}

Now do whatever you want in the backgroundTask method.

animuson
  • 53,861
  • 28
  • 137
  • 147
Vish_iOS
  • 280
  • 3
  • 17
  • Is that located in the AppDelegate, or can this be in any class? – anthoprotic Jul 12 '13 at 22:13
  • yes, this code is in AppDelegate. And i've not tried it myself, but it should work in any other class too... – Vish_iOS Jul 13 '13 at 07:01
  • There must be something I don't understand, because if I put this code and leave the method backgroundTask empty, my app runs in the background. If I remove your code, it doesn't anymore. – anthoprotic Jul 14 '13 at 00:14
  • what do you mean by, your app doesn't run in background if you remove the code.?? Did you check by putting an log in backgroundTask method? – Vish_iOS Jul 15 '13 at 05:31
0

You can use for running app in background for particular task.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    UIApplication *app = [UIApplication sharedApplication];
    UIBackgroundTaskIdentifier bgTask = 0;

    backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(backgroundTask) userInfo:nil repeats:YES];

    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
    }];
}
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
Dinesh Patel
  • 142
  • 1
  • 7
  • This appears to be the exact same solution that @Vish_Obj-C posted previously. What am I missing? – smholloway Oct 02 '13 at 20:22
  • Code looks wrong. The block will capture the value of `bgTask` when it is created, which is 0, leading to an incorrect `[app endBackgroundTask:0]`. Instead, you probably want to have a bgTaskID ivar/property, so the assignment can propagate to the block. – Clay Bridges Oct 16 '13 at 20:33