Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why beacons cause Bluetooth to continuously toggle?

I am experiencing a really weird bug working with iOS and iBeacon. I have a really simple BeaconManager that ranges beacons with particular UUID, major and minor values and performs some actions once it found them. My app seems to work properly until it continuously toggle the Bluetooth status and stop doing its job. The only visible result is that the Bluetooth icon in the status bar start flickering due to Bluetooth stopping and restarting.

Where to focus attention?

This is my class definition:

#import "BeaconManager.h"

@implementation BeaconManager

- (instancetype)init {
    self = [super init];
    if (self) {
        NSURL *beep = [[NSBundle mainBundle] URLForResource:@"beep" withExtension:@"aiff"];
        soundFileURLRef = (CFURLRef) CFBridgingRetain(beep);
        AudioServicesCreateSystemSoundID(soundFileURLRef, &soundFileObject);

        // Initializes properties
        beacon = [CLBeacon new];
        foundBeacons = [NSMutableArray new];
        _lastBeaconActionTimes = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)initRegion {
    // Initializes the beacon region by giving it an UUID and an identifier
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:BEACON];
    beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"beacon.region"];
    // Starts looking for beacon within the region
    [self.locationManager startMonitoringForRegion:beaconRegion];
}

- (void)checkBeacon {
    if (!self.locationManager) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;

        if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
            [self.locationManager requestWhenInUseAuthorization];
    }
    [self initRegion];
    [self locationManager:self.locationManager didStartMonitoringForRegion:beaconRegion];
}

#pragma mark - CLLocationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    [self.locationManager startMonitoringForRegion:beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    [self.locationManager stopRangingBeaconsInRegion:beaconRegion];
}

- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    NSLog(@"Failed monitoring region: %@", error);
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSLog(@"Location manager failed: %@", error);
}

- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region {

    if (foundBeacons.count == 0) {
        for (CLBeacon *filterBeacon in beacons) {
            // If a beacon is located near the device and its major value is equal to 1000 (MAJOR constant)
            if (((filterBeacon.proximity == CLProximityImmediate) || (filterBeacon.proximity == CLProximityNear)))
                // Registers the beacon to the list of found beacons
                [foundBeacons addObject:filterBeacon];
        }
    }
    // Did some beacon get found?
    if (foundBeacons.count > 0) {
        // Takes first beacon of the list
        beacon = [foundBeacons firstObject];

        if (([beacon.major isEqualToNumber:[NSNumber numberWithInt:MAJOR]]) && ([beacon.minor isEqualToNumber:[NSNumber numberWithInt:MINOR]])) {
            // Takes the actual date and time
            NSDate *now = [[NSDate alloc] init];
            NSString *key = [NSString stringWithFormat:@"%@ %@ %@", [beacon.proximityUUID UUIDString], beacon.major, beacon.minor];
            NSDate *lastBeaconActionTime = [_lastBeaconActionTimes objectForKey:key];
            if ((lastBeaconActionTime == nil) || ([now timeIntervalSinceDate:lastBeaconActionTime] > MINIMUM_ACTION_INTERVAL_SECONDS)) {
                [_lastBeaconActionTimes setObject:now forKey:key];
                // Plays beep sound
                AudioServicesPlaySystemSound(soundFileObject);

                if (self.delegate) {
                    // Performs actions related to the beacon (i.e. delivers a coupon)
                    [self.delegate didFoundBeacon:self];
                }
                self.locationManager = nil;
            }
            // else [self.locationManager stopMonitoringForRegion:region];
        }
        [foundBeacons removeObjectAtIndex:0];
        beacon = nil;
    }
}
@end
like image 269
Dree Avatar asked Jul 10 '15 10:07

Dree


People also ask

Can beacons access data on your phone?

Your phone also allows you to deny an app access to contact you via notifications. Beacons themselves don't collect any data. They do not send marketing messages to your phone. They broadcast location marks that your phone and apps using your phone can take advantage of to understand more precisely where you are.

How do Bluetooth beacons work?

Bluetooth beacons work by transmitting packets of data that are picked up by a compatible receiving device via radio waves. These packets of data are either self-contained or are triggers to events on the receiving device such as push notifications, app actions, and prompts.

What is a relay Bluetooth beacon?

>A Bluetooth beacon is a small radio transmitter. A Bluetooth beacon is a small wireless device that works based on Bluetooth Low Energy. It's kind of like a lighthouse: it repeatedly transmits a constant signal that other devices can see.

Does iBeacon require Bluetooth?

Yes, Android requires Bluetooth permission to scan for bluetooth beacons but iOS does not require the same permission to detect iBeacon. The requirement for Android to have Bluetooth permission to scan for beacons is true regardless of the library you use. It is an OS platform restriction.


1 Answers

Can't say for sure this is the reason why Bluetooth keeps toggling, but this part is definitely suspicious:

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    [self.locationManager startMonitoringForRegion:beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];
}

This is essentially an infinite loop. Once monitoring starts, iOS invokes the didStartMonitoring method … which starts monitoring for the very same region, which makes the iOS invoke the didStartMonitoring method again, which …

I'd start with removing the startMonitoringForRegion line from this part of your code.

like image 139
heypiotr Avatar answered Sep 21 '22 23:09

heypiotr