iBeacon: didRangeBeacons stops getting called, must reset device for it to work again

Patrick picture Patrick · Apr 8, 2014 · Viewed 10.4k times · Source

I am using a custom BeaconManager delegate so that beacon ranging is not determined by the life-cycle of the view controller. Everything works great but every once in a while (1-2 days) beacon ranging will stop working and didRangeBeacons will never get called. The only way to fix this is for me to reset my iPhone, once I do this, it works perfectly. Below is the code that I am using. The basic flow is that when my ViewController calls ViewDidLoad it sends a notification back to the AppDelegate to tell it to start ranging for beacons, I never tell it to stop then because I want it to continue to range for beacons no matter where the user navigates to in the app. I'm wondering if my code is causing this or if this is just a bug with Bluetooth. Thanks for your help!

BeaconManager.m

#import "BeaconManager.h"
#import "AppDelegate.h"

@interface BeaconManager()<CLLocationManagerDelegate>

@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CLBeaconRegion *beaconRegion;

@end

@implementation BeaconManager

+ (id)sharedManager
{
    static BeaconManager *sharedBeaconManager = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        sharedBeaconManager = [[self alloc] init];
    });
    return sharedBeaconManager;
}

- (id)init
{
    self = [super init];
    if(self)
    {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
    }
    return self;
}

- (void)startBeaconMonitoring:(NSString*)forUUID
{
    NSUUID * uuid = [[NSUUID alloc] initWithUUIDString:forUUID];

    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"com.beacons.publicRegion"];
    [self.locationManager startMonitoringForRegion:self.beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}

- (void)stopBeaconMonitoring
{
    //Stop the region monitoring
    if(self.locationManager != nil && self.beaconRegion != nil) {
        [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
    }
}

#pragma mark - CLLocationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
    self.beacons = beacons;
    if(self.delegate != nil) {
        [self.delegate beaconManager:self didRangeBeacons:self.beacons];
    }
}

@end

ViewController.m

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"startRanging" object:nil userInfo:nil];
}

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startRangingForZombies) name:@"startRanging" object: nil];

    return YES;
}

- (void)startRanging
{
    //Start the beacon region monitoring when the controller loads
    BeaconManager *beaconManager = [BeaconManager sharedManager];
    beaconManager.delegate = self;
    [beaconManager startBeaconMonitoring:@"1234-54324-34242-34242-43243"];
}

Answer

davidgyoung picture davidgyoung · Apr 9, 2014

We have received many reports at Radius Networks of phones stopping detecting iBeacons and requiring a reboot or turning Bluetooth off and back on again to resolve the situation. Folks have reported this on iPhone 4S, iPhone 5s, iPhone 5c and iPads.

I do not have any hard evidence that this is something that broke as of iOS 7.1, but the report frequency has gone way up since its release. The circumstantial evidence is therefore pretty strong.

When this phone gets into this state, the phone can still scan for bluetooth devices, and can still transmit as an iBeacon. It is therefore not a hardware problem with Bluetooth. Based on the available evidence, it is most likely a newly introduced bug in CoreLocation.