I\'m using the air locate example and monitoring for iBeacons by uuid only. When I get the entered region event, I can\'t seem to get the major and minor from the beacon/re
You're not doing anything wrong. Surprising as it may seem, the monitoring API doesn't give you the specific beacon(s) that triggered the region change.
The reason the major isn't set on the CLBeaconRegion object is because that is the exact same object you used to start monitoring, and you set that field to nil (or didn't set it at all leaving it nil). What you are looking for is an additional array of CLBeacon objects. And as you suggest, this is only present on the Ranging APIs.
It really isn't a big deal to start ranging. Just set it up at the exact same time as you start monitoring:
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:@"blah"];
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
region.notifyEntryStateOnDisplay = YES;
[self.locationManager startMonitoringForRegion:region];
[self.locationManager startRangingBeaconsInRegion:region];
And if you only care about the first ranging call, you can use a flag to ignore further updates:
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
if (!_firstOneSeen) {
// Do something with beacons array here
}
}
And reset that flag when you leave the region
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
_firstOneSeen = NO;
}
As a bonus, this will also make your monitoring response times much faster when your app is in the foreground. See: http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html
CLBeaconRegion is the filter criteria to find beacons. So what you pass in is what you are going to get back when "didEnterRegion" delegate fires. No surprises there.
CLBeacon is the individual beacon that has to fit the filter criteria to fire the didEnterRegion delegate. So here is where you will find the major, minor values. To get the beacon that fired you need to call the ranging API startRangingBeaconsInRegion and give it a count of 5 before you stop ranging. Do this after you get didEnterRegion callback. This is catered towards background monitoring for beacons, when you do not want to launch your app. but just note down when a user passes by a beacon for building intelligence on the server for a better targeted Ad campaign.
Sometimes ranging might not find any beacons, in that case use the beaconRegion in the callback for startRangingBeaconsInRegion to note down that someone entered a beacon region.
Unfortunately, determining the major and minor is only available in the iBeacon ranging API and not the iBeacon monitoring API if you monitored by UUID only. Furthermore,
1) iBeacon ranging does not work while your app is in the background. This means if your app is in the background (which is most of the time for these types of apps) you cannot determine the major and minor of an iBeacon unless you monitored based on UUID, major and minor.
2) You can only monitor a maximum of 20 iBeacons at the same time. This means if you resort to monitoring by UUID, major and minor (to get around the ranging in the background issue) you will be limited to only 20 iBeacons (a big limitation in most practical cases).
3) For maximum scalability (since you can only concurrently monitor 20 iBeacons) it would be best to monitor by UUID only, notify the user when an iBeacon of that UUID is detected and upon notification the user could acknowledge to bring the app into the foreground. Once the app is in the foreground ranging can occur to determine the major and minor of the iBeacon.
All of this leads me to wonder why Apple didn't include the array of iBeacons triggering the entry/exit in the monitoring API in the first place. I leave this for Apple to comment on. The following post goes into great detail on these iBeacon behaviors/limitations - iBeacon in the background - Use cases