Search code examples
iosiphonebluetoothibeacon

iBeacons ranging in background sometimes has delay


I have an iBeacons app able to range for beacons while in background or not running. I implemented UILocalNotifications and they work fine, meaning i get a notification when i reach the range of a beacon.

Not having a real beacon i created an app (for another device, let's say an iPad for the next scenario) that acts like 2 different beacons, meaning it can broadcast 2 different signals, same UUID but different Major/Minor values (call this beacon A and B), one at a time obviously. My problem is in this scenario:

  1. Have my iPhone (with iBeacons app closed) in lock screen
  2. Activate my iPad app, broadcasting beacon A
  3. My iPhone reacts showing me a notification
  4. I stop iPad app from broadcasting beacon A, wait 1 second, start broadcasting beacon B
  5. My iPhone DOESN'T react
  6. I stop iPad from broadcasting
  7. A few minutes later (about 2) my iPhone shows me the notification of beacon B

Now what i don't understand is this delay, the first time my iPhone reacts immediately, the second time it takes about 2 minutes to notify me the beacon.

If, after beacon B notification, i re-start broadcasting a beacon (A or B) my iPhone reacts immediately, then for the next time it always waits for 2 minutes.

Why is this happening? I've read some article saying that it's because the bluetooth awakes every 2-4 minutes while the app is in background, so i can get the info not faster than this time. But i don't see much sense in this because whenever i get the second notification the broadcasting of the beacon (B in my scenario) was already stopped, it means that if the bluetooth awakes in that very moment no beacon was in the air! But i get the notification, so it means that in someway my iPhone found it before i stopped the broadcasting.

Is this a problem that can be solved?

EDIT with some code

Here is my viewDidLoad

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Initialize location manager and set ourselves as the delegate and beacons dictionary
    _beacons = [[NSMutableDictionary alloc] init];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    // Create a NSUUID with the same UUID as the broadcasting beacon
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"6C1AA496-1653-403D-BD1E-7F630AA6F254"];

    // Setup a new region with that UUID and same identifier as the broadcasting beacon
    self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
                                                             identifier:@"testregion"];

    NSLog(@"startMonitoring");
    // Tell location manager to start monitoring for the beacon region
    [self.locationManager startMonitoringForRegion:self.myBeaconRegion];
    [self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];

    _myBeaconRegion.notifyEntryStateOnDisplay = YES;

    // Check if beacon monitoring is available for this device
    if (![CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Monitoring not available" message:nil delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil]; [alert show]; return;
    }

}

Now whenever i get a beacon i send a notification, i just wanted to try how it worked so i didn't implemented yet a way to send just 1 notification, it means i get about 9 notification, 1 per sec that's the active time the code can run while in background i suppose (1 second enter region, 9 ranging for beacons)

-(void)locationManager:(CLLocationManager*)manager
       didRangeBeacons:(NSArray*)beacons
              inRegion:(CLBeaconRegion*)region
{
    if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground){
        UILocalNotification *notification = [[UILocalNotification alloc] init];
        notification.alertBody = @"Found Beacon";
        notification.soundName = @"Default";
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
    }
}

Actually to be more specific, if i totally close my app from the multitasking view, or just let it in background, as soon as i start broadcasting a beacon i get the notification**S** (1 second delay). Then stopping the broadcasting and re-play the delay becomes in the order of minutes.

Now for a real scenario, where i should have many beacons in the same place, this delay could be a problem, as long as i get the notification when i could be already far away from the beacons itself.

Has my code any problem? I read those articles but i never found a delay of 15 minutes.

EDIT2 after davidgyoung suggestions

I modified my code and as you said using 2 different regions for beacon A and B the delay is always null. I also logged with the piece of code you gave and i discovered this.

  1. Broadcast the beacon of Region_1
  2. Device shows me the notification of Region_1
  3. Stop broadcasting the beacon of Region_1
  4. The logs say i'm still in the region, only after a couple of minutes i get the log "OUTSIDE Region_1", just now i can re-play the broadcasting to get another notification from Region_1.

So i was curious about this and i read and article http://beekn.net/2014/03/apple-ios-7-1-launches-major-ibeacon-improvement/

The author says that from iOS 7.1 the responsiveness of exiting a region is immediate, actually i'm running 7.1 but i also have a couple of minutes delay. Why this? Did you find the same problem in your tests?

Now, i read that a device can listen for no more than 20 regions right?it means that if i have 100 beacons i can set up just 20 regions and divide these 100 in 20 groups, getting no more than 20 notification (assuming these 100 are in the same place, all in the range of my device) ? That could be a problem because will force the user to run the app in the foreground to get all the information (assuming again each of the 100 beacons have a particular and unit role), am i right?


Solution

  • EDIT: This is my first answer before seeing the code.

    I cannot explain this behavior based on your description, but I suspect there may be an issue with the setup that is either delaying your local notification or inaccurately reporting the state of beacon region B when the notification is sent.

    Two things would help verify/eliminate this possible cause:

    1. Add NSLog statements in your didDetermineState: forRegion: callback like below, then repeat your test reporting the log results.

      // put this in your AppDelegate
      - (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
      {
          if(state == CLRegionStateInside) {
              NSLog(@"locationManager didDetermineState INSIDE for %@", region.identifier);
          }
          else if(state == CLRegionStateOutside) {
              NSLog(@"locationManager didDetermineState OUTSIDE for %@", region.identifier);
          }
          else {
              NSLog(@"locationManager didDetermineState OTHER for %@", region.identifier);
          }
      }
      
    2. Post the code that sets up monitoring and issues the notification on detection.

    If you have not read this already, you may want to give a quick look to similar tests I have done to measure background detection times:

    http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html

    http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html