iBeacon: get major and minor - only looking for uuid
Asked Answered
R

3

7

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/region that has triggered the event if I'm only looking for the uuid (I can if I'm monitoring for a uuid with specified major and minor) - does anyone know a way to do this/am I missing something?

I don't really want to start ranging - doesn't seem like I should need to..

(The use case is for say lots of stores all with beacons with the same uuid, then issuing an OS notification with relevant information about that store (obtained by querying the major and minor))

Here's basically what I do:

CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:@"blah"];
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
region.notifyEntryStateOnDisplay = YES;

[self.locationManager startMonitoringForRegion:region];

Then in the app delegate:

- (void) locationManager:(CLocationManager*)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion*)region {

    // assume for now its the iBeacon
    CLBeaconRegion *beaconRegion = (CLBeaconRegion*) region;

    beaconRegion.major  // hasn't been set...

}

Many Thanks!

Reginiaregiomontanus answered 21/11, 2013 at 15:3 Comment(0)
C
20

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

Corny answered 22/11, 2013 at 3:6 Comment(4)
Thanks a lot David, that's really helpful. Also thanks for your other SO answers which have been invaluable.Reginiaregiomontanus
Good post. One minor correction however. The region object you get in didEnterRegion/didExitRegion calls is not the same CLBeaconRegion object that you used to register - it's a copy. I know this because at first I tried using code of the form if (region == regionImTracking) and the comparison failed. I logged the two regions and their addresses were different, while all their settings matched.Lennyleno
Is there any official documentation where it explicitly says it is not possible? I know it us true but I have to explain it to management and point to something else than just people "making assumptions". ThanksToffic
The official documentation may not say this explicitly enough to be understandable to all managers, but it does say it. See the locationManager:didDetermineState:forRegion: section on the followin g page, and note that it describes the Region object passed as "The region whose state was determined." This region object does not contain iBeacon identifiers. developer.apple.com/library/ios/documentation/CoreLocation/…Corny
M
4

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

Mho answered 12/2, 2014 at 16:18 Comment(1)
You might want to review the comments on ranging in the background at the question you link.Artery
C
0
  • 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.

Compaction answered 18/8, 2014 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.