title: Why does beginBackgroundTaskWithExpirationHandler allow an NSTimer to run in the background?
body: <p>I know there are a lot of questions on similar topics, but I don't think any quite address this question (although I'm happy to be proved wrong!).</p>
  
  <p>First some background; I have developed an app that monitors user location in the background and this is working fine, but battery usage is high. To try and improve this, my objective is to 'wake up' every x minutes, call <code>startUpdatingLocation</code> get a position, then call <code>stopUpdatingLocation</code>.</p>
  
  <p>Consider the following sample that I put together for testing:</p>
  
  <pre><code>@interface ViewController ()
  @property (strong, nonatomic) NSTimer *backgroundTimer;
  @property (strong, nonatomic) CLLocationManager *locationManager;
  @end
  
  @implementation ViewController
  
  - (void)viewDidLoad {
      [super viewDidLoad];
  
      // Add observers to monitor background / foreground transitions
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
  
      _locationManager = [[CLLocationManager alloc] init];
      [_locationManager requestAlwaysAuthorization];
      _locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
      _locationManager.delegate = self;
      _locationManager.distanceFilter=100;
      [_locationManager startUpdatingLocation];
  
  
      NSTimeInterval time = 60.0;
      _backgroundTimer =[NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startLocationManager) userInfo:nil repeats:YES];
  
  }
  
  -(void)applicationEnterBackground{
      NSLog(@"Entering background");
  }
  
  -(void)applicationEnterForeground{
      NSLog(@"Entering foreground");
  }
  
  -(void)startLocationManager{
      NSLog(@"Timer fired, starting location update");
      [_locationManager startUpdatingLocation];
  }
  
  -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
      NSLog(@"New location received");
      [_locationManager stopUpdatingLocation];
  }
  
  - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];    
  }
  @end
  </code></pre>
  
  <p>As expected, when the application enters the background the <code>CLLocationManager</code> is not updating location, so the <code>NSTimer</code> is paused until the application enters the foreground.</p>
  
  <p>Some similar SO questions have asked about keeping NSTimers running in the background, and the use of <code>beginBackgroundTaskWithExpirationHandler</code> so I added this to the <code>viewDidLoad</code> method just before the <code>NSTimer</code> is started.</p>
  
  <pre><code>UIBackgroundTaskIdentifier bgTask = 0;
  UIApplication *app = [UIApplication sharedApplication];
  NSLog(@"Beginning background task");
  bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
      NSLog(@"Background task expired");
      [app endBackgroundTask:bgTask];
  }];
  NSLog(@"bgTask=%d", bgTask);
  </code></pre>
  
  <p>Over short tests (whether it works long term is yet to be proved) this seems to address the issue, but I don't understand why.</p>
  
  <p>If you take a look at the log output below, you can see that calling <code>beginBackgroundTaskWithExpirationHandler</code> achieves the desired objective as the <code>NSTimer</code> continues to fire when the app enters the background:</p>
  
  <pre><code>2015-03-26 15:45:38.643 TestBackgroundLocation[1046:1557637] Beginning background task
  2015-03-26 15:45:38.645 TestBackgroundLocation[1046:1557637] bgTask=1
  2015-03-26 15:45:38.703 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:45:46.459 TestBackgroundLocation[1046:1557637] Entering background
  2015-03-26 15:46:38.682 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
  2015-03-26 15:46:38.697 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:47:38.666 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
  2015-03-26 15:47:38.677 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:48:38.690 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
  2015-03-26 15:48:38.705 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:48:42.357 TestBackgroundLocation[1046:1557637] Background task expired
  2015-03-26 15:49:38.733 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
  2015-03-26 15:49:38.748 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:50:38.721 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
  2015-03-26 15:50:38.735 TestBackgroundLocation[1046:1557637] New location received
  2015-03-26 15:50:44.361 TestBackgroundLocation[1046:1557637] Entering foreground
  </code></pre>
  
  <p>From this, you can see that the background task expires after approximately 3 minutes as expected, but the NSTimer continues to fire after that.</p>
  
  <p>Is this behaviour expected? I will run some more tests to see if this solution works in the long term, but I can't help but feel I'm missing something with the use of the background task?</p>