iBeacon Tutorial: Dealing with ‘Errors’

iBeacon developers are the Matt Damon of coding: incredibly smart, imaginative people but deep down inside they need someone to hug them and tell them “It’s not your fault.”

If you’ve been playing with Apple’s iBeacon(TM) technology you’ll know what I mean. Maybe you followed the tutorial on DevFright and checked out Cocoanetics once you got your Estimote kit; you played around with an SDK or two and checked out how to range your beacons with HiBeacon.

Then you go to test your app and you hit a wall.

But don’t worry: in all likelihood it’s not your fault.

Problem Triggering LocalNotification? 

Your app enters a beacon region and is supposed to trigger a local notification. You’re using UILocalNotification with didEnterRegion but it doesn’t seem to work, or it seems to trigger once you leave instead of when you enter.

It’s not your fault: what might seem like an error in ranging is actually just the Apple API taking a seemingly random amount of time to deliver a notification. We’ve yet to define a clear logic for delays: in the same circumstances we’ll see notifications triggered quickly or minutes later.

Repeat Notifications? 

The second problem is that notifications are being triggered, but they’re being triggered multiple times. But you’ve only walks into a region once! Is there some kind of loop you built into your code?

It’s not your fault: whether you want to blame interference or Apple, your app will randomly trigger a didExitRegion event because it loses the signal. This is almost surely a bug – and has been reported to Apple with no sign yet of a response. (Apple recommends you clear the notification queue once you’ve queued one up for delivery).

CLProximity Toggling? 

So you have your app – you move towards a beacon. Everything is going fine. It shows it as far, then near, then immediate. You’re standing RIGHT NEXT to the beacon. And suddenly it toggles to “Near” again. There must be something wrong with the beacon! Or your code.

No, it’s not your fault: Behind the scenes, Apple is doing some kind of fancy math to average out the signal your app gets from beacons in order to place it within a CLProximity range. But it gets it wrong. And it gets it wrong right after it gets it right before it gets it wrong again. Um, or something. So by relying on Apple’s back-end, unknown logic, you’ll see sudden rapid toggles between proximity regions. This might be due to signal interference but another way to look at it is that Apple has put too high a sensitivity on region changes rather than smoothing out the curve for a slightly longer verification cycle.

My App Doesn’t Seem to Know When It Exits a Region. 

Your app responds beautifully once it’s in range of beacons and switches gracefully between CLProximity values. (OK, well, other than the previous point). But then you go to exit a region and your app seems to ‘hang’ for a bit before it knows it left the building.

Guess what? It’s not your fault: Apple might be overly sensitive about CLProximity value changes, but it’s under-sensitive on region exits. While it will toggle unexpectedly between proximity values, it will take its time making absolutely sure you left the region. So, you’ll get a long delay before your app will ‘accept’ that it has truly left a region. Apple says this is a design decision that’s meant to make a seamless user experience.

But Hold On, My App Never Left the Region! 

Hand-in-hand with this, however, is that while sometimes it seems like it takes forever to leave a region, it also sometimes unpredictably exits either really fast or when I’m still near a beacon.

It’s not your fault: This is a fairly clear Apple bug. Check out the little “location” arrow on the top of your iPhone or iPad screen. You’ll see it suddenly disappear. A bug seems to randomly turn off location services which can lead to a false positive for either didExitRegion or location services being unavailable.

My App Doesn’t Know Which Beacon to See

Without hard coding some kind of logic around your beacon’s major/minor values (forcing your app, for example, to see them ‘in sequence’ as the user moves through a room) you’re probably relying on something like this to determine the ‘closest’ beacon:
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];

But even if you’re prioritize “nearest” beacons, you still end up with toggling between beacons, and so you spend all day trying to move your beacons around so they don’t compete with each other. But you STILL have problems!

Sing along now! It’s not your fault: As noted above, Apple does calculations behind the scenes, has problems calculating CLProximity which gives rapid toggles, and has an obscure logic to how it calculates lastObject. This combination of factors, along with room interference, causes all kinds of headaches in prioritizing beacons. You’re not working here with clean logic or technology – so you can expect to work in both code, beacon placement, and beacon settings to get an ideal installation.

My App Won’t Wake Up to Beacons

Location has worked before. I could get my app to do stuff, trigger an alert…but I can’t seem to ‘find beacons’ or even location and get my app working.

It’s Not….Whose? Yes! It’s Not Your Fault Apple has decided, with iOS 7, that what a user REALLY means when they hard close an app is that they really really don’t want that app doing anything. So NO….your app will NOT wake up if it’s closed. It will only work if it’s still in the background (i.e. in the user’s app tray).

Take Heart, Good Will Is What We Need

So take heart: your hunt for the perfect iBeacon app shouldn’t be abandoned before you begin! It’s not your fault that it doesn’t seem to be working the way you expected – and it’s not usually the fault of the beacons either.

List out all these little bugs and weird ways that Apple handles the iBeacon API and design your app against an actual use case. Think through the user experience. Then map that to the known limitations and bugs.

You’ll be surprised how easy it is to handle a lot of this stuff: a clever pushViewController here, a snippet of a timer there, and you’re good to go on the path to iBeacon mastery. Because for all the frustrations you might be having, truly: it’s not your fault.

Any tips you’d like to share? Frustrations you’ve had? Would love to hear them. Drop them in the comments below.

Let’s Talk

Let’s chat on Twitter! We have some good conversations. Or join our weekly mailing list for ‘BEEKn unplugged’ – I rant a little each Friday and share stuff that doesn’t make it on the site.

Be the Beacon!

20 Responses to “iBeacon Tutorial: Dealing with ‘Errors’”

  1. “It’s Not….Whose? Yes! It’s Not Your Fault Apple has decided, with iOS 7, that what a user REALLY means when they hard close an app is that they really really don’t want that app doing anything. So NO….your app will NOT wake up if it’s closed. It will only work if it’s still in the background (i.e. in the user’s app tray).”

    Doug, from my tests, this is not absolutely true. I have had two instances when I had hard closed my app, but the app awoke some time later with a region notification. It’s extremely rare, but it happens. Even when my app is just in the background though, the notifications are unpredictable. This has to be fixed to make ibeacons useful, IMO.

    Reply
  2. So we’re not the only ones!

    We discovered the same thing but mostly with local notifications. I’m not absolutely certain but it seems like what’s happening is that the local notification gets called before you close the app, but it gets sent sometime later. I agree – this is a SERIOUS bug or problem – we’ve had cases where with the app closed off, we get a local notification 20 or 40 minutes later. Rendering it useless IMO.

    So my take? No, the app won’t do anything if it’s closed. But beware! Notices might get called, you then close the app, and then the notice is GIVEN much later (by which time, you’re no longer at Macy’s, you’re at Nordstroms or wherever).

    Agree – needs to be fixed. Otherwise we won’t include it in our user scenario at all except at the most generic level “thanks for being part of our experience” rather than “thanks for looking at shoes”.

    Reply
  3. That sounds like a very possible explanation to what may be happening. Delayed local notifications that get triggered after the app has been closed. Maybe stuck in a queue or something, since so many errant local notifications are being generated. A log jam. Hmm.

    Thanks for the insight.

    Reply
  4. I thought it was just me losing the plot. I couldn’t get the app to notice it was in a region for ages earlier, and then when it did it picked up an identifier (com.company part) from a test I’d done an hour earlier. Then it would enter and exist as I turned a beacon on and off, but only once. Crazy.

    Reply
  5. Indeed – we’re just starting. But what about OTHER devices, technologies and platforms? There’s this thing called Wifi. You might have heard about it.

    Or is this exclusively a “fanboy” site?

    Reply
  6. Actually, Marc, we’ve written extensively about ‘hybrid’ solutions. Yes, we’re fans of BLE, but mostly because of their power to help rethink physical/digital and how you need a set of technologies to create a solution – WiFi included.

    Having said that, we’re not about to become a site that covers ALL technology – you can always read Gigaom or re/Code or something if that’s your interest.

    Reply
  7. The best result in sorting out Beacons I got with this way of handling:

    -(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
    {
    if (beacons.count>0) {
    CLBeacon* closestBeacon = nil;
    for (CLBeacon* beacon in beacons) {
    if ((beacon.rssi0.0f) ) {
    if (closestBeacon == nil) {
    closestBeacon = beacon;
    } else if ((beacon.rssi>closestBeacon.rssi)&&(beacon.accuracy<closestBeacon.accuracy)){
    closestBeacon = beacon;
    }
    }
    }
    if (closestBeacon) {
    //Do something with the closest Beacon
    } else {
    //Do something when no Closest Beacon found.
    }
    }
    }

    Despite of dealing with unavoidable radio reflections and interferences I have had good success with this procedure.
    Altough I never check for didEnterRange and didExitRange. Since my very App only will run in one dedicated area I immediately call "_locationManager startRangingBeaconsInRegion:" after starting the app.
    But Yeah… It is really frustrating holding the iDevice as close as one Millimeter to a Beacon and the crappy system finds for minutes only another Beacon that is two meters away… Then I get a slightly uncomfortable feeling arising in me because it seems to be just another proof that Apple is on it's way to be the new Microsoft when it comes to software quality…

    Reply
    • Here again the Snippet hopefully propperly formatted. :)


      -(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
      {
      if (beacons.count>0) {
      CLBeacon* closestBeacon = nil;
      for (CLBeacon* beacon in beacons) {
      if ((beacon.rssi0.0f) ) {
      if (closestBeacon == nil) {
      closestBeacon = beacon;
      } else if ((beacon.rssi > closestBeacon.rssi)&&(beacon.accuracy<closestBeacon.accuracy)){
      closestBeacon = beacon;
      }
      }
      }
      if (closestBeacon) {
      //Do something with the closest Beacon
      } else {
      //No Closest Beacon found.
      }
      }
      }

      Reply
  8. The comparison to Matt Damon was ingenious. I’m still trying to figure out a way to get didExitRegion to execute more quickly and accurately, I seem to be getting a 20-30 second delay before it’s called :/

    Reply
  9. Erik –

    The only way to truly overcome the issue is to not use didExitRegion. didExit is fired when you’re no longer in range of the UUID. However, you’ll find that you can get a far more accurate picture of whether a specific beacon is nearby.

    If you keep track of nearby Major/Minor values then at some point, when you walk away from the beacon, the Major value will ‘disappear’ from your list. Put in a counter for a few seconds to make sure it’s not a false reading but if you don’t “hear” that major value again then you can consider it to be an ‘exit’.

    The problem is with didExitRegion.

    Unfortunately, there’s nothing you can do about how Apple has ‘baked in’ their decisions on how quickly to consider a region truly exited.

    The only solution is to keep an eye on the beacon major values, when one drops off consider it “exited”, but to make sure that you put a brief timer in place because you’ll have very brief false “drop offs” from your list of beacons.

    Hopefully this helps?

    Reply
  10. Manu Jose

    I am monitoring 3 regions . But did enter region and didexitregion methods are repeated triggered even though app is inside the region. I need notifications only when app enters or exits any of the 3 region. Is this happening because i am testing the app repeatedly

    Here is my code:

    – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    STCloudManager setupAppID:@”valid is” andAppToken:@”valid token”];

    UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

    if (locationNotification) {
    // Set icon badge number to zero
    application.applicationIconBadgeNumber = 0;
    }

    self.beaconManager1= [[ESTBeaconManager alloc]init];
    self.beaconManager1.delegate = self;
    self.beaconManager1.avoidUnknownStateBeacons=YES;
    self.beaconManager1.preventUnknownUpdateCount=YES;
    NSSet *set=[self.beaconManager1 monitoredRegions];
    self.region_desk=[[CLBeaconRegion alloc]
    initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID major:36798 minor:29499
    identifier:@”Appdelegate_Desk_Beacon_Region”];

    self.region_door1=[[CLBeaconRegion alloc]
    initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID major:29666 minor:63757
    identifier:@”Appdelegate_Door_Beacon1_Region”];

    self.region_door2=[[CLBeaconRegion alloc]

    initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID major:64157 minor:33188
    identifier:@”Appdelegate_Door_Beacon2_Region”];

    [self.beaconManager1 requestAlwaysAuthorization];

    //already monitoring
    if ([set count]<3) {

    if (![set containsObject:self.region_desk]) {

    [self.beaconManager1 startMonitoringForRegion: self.region_desk];
    }

    //
    if (![set containsObject:self.region_door1]) {

    [self.beaconManager1 startMonitoringForRegion: self.region_door1];
    }
    if (![set containsObject:self.region_door2]) {

    [self.beaconManager1 startMonitoringForRegion: self.region_door2];
    }

    }

    return YES;
    }

    Reply

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>