Close iBeacons monitoring on background and with app closed

Im building a simple ios app with IBeacon, Im using startMonitoringForRegion for detect beacons. Thats AppDelegte.m code:

    NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:@"0040C159-12F6-4FC3-9189-87C069FFE5CF"];
    NSString *regionIdentifier = @"iBeacons region 1";
    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: beaconUUID identifier: regionIdentifier ];
    self.beaconRegion.notifyEntryStateOnDisplay = YES;
    self.locationManager = [[CLLocationManager alloc]init];
    self.locationManager.delegate = self;
    self.locationManager.pausesLocationUpdatesAutomatically = NO;
    [self.locationManager startMonitoringForRegion:self.beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];

Then on didDetermineState callback:

- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
    
    if (state == CLRegionStateInside) {
        //Start Ranging
        [manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
        NSLog(@"didDetermineState -->enter region");
    }
    else{
        //Stop Ranging
        [manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
        NSLog(@"didDetermineState -->exit region");
    }
    
}

This is working fine on foreground, background or with the app closed when the iBeacons are far each other. But i detect some problems when the iBeacons are close each other.

After performing several tests I have concluded that RangingBeaconsInRegion doesn’t working on background or with app closed, only monitoring works.

The app will be launched into the background for about 5 seconds when you enter or exit the region of a beacon, and it will get a callback to the didDetermineState: forRegion: method. You can here do ranging in the background for this five second period, after which iOS will suspend your app again.

This is perfect when iBeacons are far from each other. The problem comes when iBeacons are too close and the callback didDetermineState: not called for the exit of the iBeacon region before you enter the next iBeacon region.

Is there any way to force the region exit manually? Or some other way to tackle this problem?

Thanks!

Two ideas:

  1. Put the beacons that are close enough into two separate beacon regions. This way, the enter and exit events will be triggered for them independently, so even if you move from one beacon to another quickly enough, you’ll still observe the “exit” event for the 1st beacon and the “enter” event for the 2nd beacon. Just keep in mind there’s a limit of 20 beacon regions that can be monitored at a time.
  2. During the 5-second period you’re given to execute code in the background, start a background execution task—last time I checked, this was giving you 3 minutes of background execution time, so that you can do ranging.

I’m trying to monitorize two regions, but I noticed something strange. When I was monitoring only one region, “didRangeBeacons” callback was firing every second. But now, with 2 regions, when both of them are in range, is firing approximately every 10 miliseconds. This is the expected behavior?
I added the code I implemented to try to monitor two regions.

but something I have done incorrectly and didRangeBeacons:InRegion: callback is firing every 10 ms when the expected is every second.

On AppDelegate.m, I’m doing the following inside didFinishLaunchingWithOptions:

[self.locationManager startMonitoringForRegion:self.beaconRegion];
        [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
        [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
        [self.locationManager startMonitoringForRegion:self.beaconRegion2];
        [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion2];
        [self.locationManager startRangingBeaconsInRegion:self.beaconRegion2];

Then, on didRangeBeacons:InRegion:

- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{

     if(beacons.count > 0){
          [self.locationManager stopRangingBeaconsInRegion:region];
          for (CLBeacon *beacon in beacons){
             NSLog(@"beacon detected major: %@ minor: %@", beacon.major,beacon.minor);
          }
           [self.locationManager startRangingBeaconsInRegion:region];   
     }

}

when I run the application on the simulator, and there is a beacon of each network in range, the message is displayed on the console approximately every 10 ms.

I suspect that the stop and restart the ranging is breaking the expected callback flow, but when only one region is in the range, callbacks occur every second as expected.

Any idea?

Hm, interesting. We’ve also noticed that sometimes, CLLocationManager has a tendency to deliver a short burst of didRange events when starting ranging—and your discovery narrows it down to cases with >1 region. Might be a good idea to report this to Apple.

In any case, what’s the reason you restart ranging in your didRange delegate?

In the future I want to make a serie of actions for each beacon that is detected within the loop, so I stop the ranging until all actions are completed.

anyway I tried to do the same without stop and start ranging and I still have the same problem when two beacons from different regions are in the range.

You mean: when two beacons from different regions are in range, the “didRangeBeacons” for both of them is called in bursts? Does it continue doing so indefinitely or goes back to once a second after some time?

Indefinitely if the beacons continue in range and the app is in foreground.

I’ll spend a couple of days testing to see if I can find the problem.

Which iOS are you on? You’re using pure Core Location, which could suggest there’s a bug somewhere in the Apple’s SDK.