Ranging Beacons only works when app running?

Aaron Bratcher picture Aaron Bratcher · Oct 2, 2013 · Viewed 26.4k times · Source

I am having difficulties getting this to work for when the app is not running. I have locationManager:didRangeBeacons:inRegion: implemented and it is called when the app is running in the foreground or background, however it doesn't seem to do anything when I quit the app and lock the screen. The location services icon goes away and I never know that I entered a beacon range. Should the LocalNotification still work?

I have Location updates and Uses Bluetooth LE accessories selected in Background Modes (XCode 5) I didn't think I needed them.

Any help greatly appreciated.

-(void)watchForEvents { // this is called from application:didFinishLaunchingWithOptions
    id class = NSClassFromString(@"CLBeaconRegion");
    if (!class) {
        return;
    }

    CLBeaconRegion * rflBeacon = [[CLBeaconRegion alloc] initWithProximityUUID:kBeaconUUID identifier:kBeaconString];
    rflBeacon.notifyOnEntry = YES;
    rflBeacon.notifyOnExit = NO;
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self.locationManager startRangingBeaconsInRegion:rflBeacon];
    [self.locationManager startMonitoringForRegion:rflBeacon];
}

-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
    if (beacons.count == 0 || eventRanged) { // breakpoint set here for testing
        return;
    }

    eventRanged = YES;
    if (backgroundMode) { // this is set in the EnterBackground/Foreground delegate calls
        UILocalNotification *notification = [[UILocalNotification alloc] init];
        notification.alertBody = [NSString stringWithFormat:@"Welcome to the %@ event.",region.identifier];
        notification.soundName = UILocalNotificationDefaultSoundName;
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
    }

    // normal processing here...
}

Answer

davidgyoung picture davidgyoung · Oct 3, 2013

Monitoring can launch an app that isn't running. Ranging cannot.

The key to having monitoring launch your app is to set this poorly documented flag on your CLBeaconRegion: region.notifyEntryStateOnDisplay = YES; This can launch your app on a region transition even after completely rebooting your phone. But there are a couple of caveats:

  1. Your app launches into the background only for a few seconds. (Try adding NSLog statements to applicationDidEnterBackground and other methods in your AppDelegate to see what is going on.)
  2. iOS can take its own sweet time to decide you entered a CLBeaconRegion. I have seen it take up to four minutes.

As far as ranging goes, even though you can't have ranging wake up your app, you can make your app do both monitoring and ranging simultaneously. If monitoring wakes up your app and puts it into the background for a few seconds, ranging callbacks start up immediately. This gives you a chance to do any quick ranging actions while your app is still running.

EDIT: Further investigation proves that notifyEntryStateOnDisplay has no effect on background monitoring, so the above should work regardless of whether you have this flag. See this detailed explanation and discussion of delays you may experience