arc4randall
arc4randall

Reputation: 3293

didExitRegion timer reset during simultaneous exits

I am building an application that works with iBeacons. I am monitoring for specific beacons, and would like to know when my phone enters and exits a region. Enters work appropriately, and I am aware that exits have a 30 second timeout associated with them. However, I am experiencing what I can only describe as a reset on the timeout when an exit occurs. Here is an example:

A second example:

I know that these beacons should be exited because I am using estimote beacons with hardware version 3.2, which enables flip to sleep mode, so the beacons stop broadcasting when upside down. Therefore, like in the case of the 2nd example, a beacon can be undetected for a full minute before registering an exit, even though I am monitoring for each beacon separately. Does anyone know if there is any way around this, or is this just a bug in iOS beacon monitoring?

What's interesting is if you exit more than 2 beacons at the same time, one of them will exit after 30, then the rest will exit 30 seconds after that, so Apple is able to detect that these different signals are exiting individually, regardless of UUID, major, and minor combinations

This is the basic functionality of my program:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:@"B9407F30-F5F8-466E-AFF9-25556B57FE6D" ] identifier:@"beacon"];
    [self.locationManager startMonitoringForRegion:region];
    [self.locationManager requestStateForRegion:region];
    self.view.backgroundColor = [UIColor redColor];
}

- (CLLocationManager *)locationManager {
    if (!_locationManager) {
        NSLog(@"location manager alloc init");
        _locationManager = [[CLLocationManager alloc] init];
        [_locationManager requestAlwaysAuthorization];
        _locationManager.delegate = self;
    }
    return _locationManager;
}

#pragma mark - CLLocationManagerDelegate

-(void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    NSLog(@"didStartMonitoringRegion: %@", region);
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    NSLog(@"didEnterRegion: %@", region);
    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
        if (beaconRegion.major && beaconRegion.minor) {
            NSLog(@"entered specific!");
        } else {
            NSLog(@"entered the general region!");
            NSLog(@"will start Ranging!");
            [self.locationManager startRangingBeaconsInRegion:beaconRegion];
        }
    }
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    NSLog(@"didExitRegion: %@", region);
    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
        if (beaconRegion.major && beaconRegion.minor) {
            NSLog(@"stop monitoring %@ %@", beaconRegion.major, beaconRegion.minor);
            [self.locationManager stopMonitoringForRegion:beaconRegion];
        } else {
            NSLog(@"will stop ranging on %@", beaconRegion.proximityUUID.UUIDString);
            [self.locationManager stopRangingBeaconsInRegion:beaconRegion];
        }
    }
}

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray<CLBeacon *> *)beacons inRegion:(CLBeaconRegion *)region {
    for (CLBeacon *beacon in beacons) {
        NSLog(@"ranging %@ %@", beacon.major, beacon.minor);
        CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beacon.proximityUUID major:beacon.major.shortValue minor:beacon.minor.shortValue identifier:[NSString stringWithFormat:@"%@ %@ %@", beacon.proximityUUID.UUIDString, beacon.major, beacon.minor]];
        if (![self.locationManager.monitoredRegions containsObject:beaconRegion]) {
            [self.locationManager startMonitoringForRegion:beaconRegion];
        }
    }
}

- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
    NSLog(@"didDetermineStateForRegion %@", region);
}

I am monitoring for a specific UUID, then when I encounter that UUID I begin ranging on it. I then start monitoring for each specific beacon that I encounter within that UUID. When I am no longer encountering any beacons with that UUID I stop ranging. This bug is bad for me because it effectively is forcing me to range for an extra 30 seconds, which I want to limit to conserve battery

Upvotes: 0

Views: 408

Answers (1)

davidgyoung
davidgyoung

Reputation: 64941

If the goal is to stop ranging sooner to conserve battery, you don't need to rely on monitoring region exits. Instead, you can simply maintain a timestamp for each detected beacon. If the timestamp of when that beacon was last detected was more than X seconds in the past, you can stop ranging. This would allow you to customize it to be even less than 30 seconds if you wish.

If you choose to go this route, one thing you should note is that if you get a ranging callback for a beacon with an RSSI of 0, then that indicates that it was not detected in the last cycle. So you would only want to update your timestamp for callbacks with a non-zero RSSI.

I realize this is more of a workaround than a direct solution to the problem you are asking about, so I offer it only if you don't find a more direct solution to the monitoring region exit timing discrepancy.

Upvotes: 0

Related Questions