I'm working on a class to handle all my iBeacon testing. It's purpose is to start looking for regions, range the beacons, identify them then send notifications. The code is below.
The problem I'm having is the app is running very slowly, I know iBeacons have latency issues, and sometimes simply stops working (won't identify a close beacon). My code is messy I know, trying to sort the logic before I work on cleaning it. I'm wondering if I have missed a logic flaw here (and by that I mean, I wonder which logic flaws I've introduced!).
#import "dcBeaconManager.h"
#implementation dcBeaconManager
#synthesize currentBeaconState;
bool testRanging = false;
int firstRegionEntered = 0;
int beaconsRangedCount = 0;
- (void)initBeaconManager {
NSLog(#"initBeaconManager called");
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"digiConsRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager requestStateForRegion:self.beaconRegion];
currentBeaconState = #"initial";
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
NSLog(#"Started looking for regions");
[self.locationManager requestStateForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"Region discovered");
if (firstRegionEntered == 0) {
NSLog(#"First time in region");
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Welcome to Digial Conversations, we are upstairs.";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
firstRegionEntered = 1;
}
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"We hope you enjoyed the event, thank you for coming.";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
NSNumber *currentBeaconMajor = beacon.major; //it's major (group) number
NSNumber *currentBeaconMinor = beacon.minor; //it's minor (individual) number
if (([currentBeaconMinor floatValue] == 59204) && ([currentBeaconMajor floatValue] == 33995) && (beacon.proximity == CLProximityNear)) {
if (beaconsRangedCount == 0) {
currentBeaconState = #"Mint";
beaconsRangedCount ++;
}
if ([currentBeaconState isEqualToString:#"Blue"] || [currentBeaconState isEqualToString:#"Purple"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateMint" object:nil];
}
} else if (([currentBeaconMinor floatValue] == 7451) && ([currentBeaconMajor floatValue] == 63627) && (beacon.proximity == CLProximityNear)) {
if (beaconsRangedCount == 0) {
currentBeaconState = #"Blue";
beaconsRangedCount ++;
}
if ([currentBeaconState isEqualToString:#"Mint"] || [currentBeaconState isEqualToString:#"Purple"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateBlue" object:nil];
}
} else if (([currentBeaconMinor floatValue] == 51657) && ([currentBeaconMajor floatValue] == 26976) && (beacon.proximity == CLProximityNear)) {
if (beaconsRangedCount == 0) {
currentBeaconState = #"Purple";
beaconsRangedCount ++;
}
if ([currentBeaconState isEqualToString:#"Mint"] || [currentBeaconState isEqualToString:#"Blue"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocatePurple" object:nil];
}
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLeaveNearRegion" object:nil];
}
}
#end
Do you mean that didEnterRegion and didExitRegion callbacks are being delayed?
If your app is running in the foreground while you are ranging, you should get entered region notifications within a second, and exit region notifications within a few seconds. If your app is in the background, it can take up to 15 minutes to get either an in region or an out of region notification.
For details on this timing, see here.
These latency issues are not beacon-specific. They have to do with the way the CoreLocation API is implemented in iOS.
Related
In my project settings > target > Capabilities I have Background Modes ON with "Location Updates" checked.
AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.storyController = [StoryController sharedController];
self.storyController.locationManager.delegate = self;
... etc initializing VC ...
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"entered region, %#", region.identifier);
[self handleRegionEvent:region];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"exited region, %#", region.identifier);
[self handleRegionEvent:region];
}
- (void)handleRegionEvent:(CLRegion *)region {
NSLog(#"handle region");
if ([UIApplication sharedApplication].applicationState ==
UIApplicationStateActive) {
NSLog(#"handle region local");
UILocalNotification *n = [[UILocalNotification alloc] init];
n.alertBody = #"LOCAL";
n.soundName = #"Default"; //Coffee Man?
[[UIApplication sharedApplication] presentLocalNotificationNow:n];
} else {
if ([[StoryController sharedController] readyForNextChapter]) {
UILocalNotification *n = [[UILocalNotification alloc] init];
n.alertBody = #"New ☕ Story";
n.soundName = #"Default"; //Coffee Man?
[[UIApplication sharedApplication] presentLocalNotificationNow:n];
}
}
}
StoryController
+ (id)sharedController {
static StoryController *myController = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
myController = [[StoryController alloc] init];
});
return myController;
}
- (id)init {
self = [super init];
self.locationManager = [[CLLocationManager alloc] init];
NSLog(#"monitored regions: %#", self.locationManager.monitoredRegions);
I am monitoring for the region later on with:
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:g.coordinate radius:1000 identifier:"My Store"];
region.notifyOnEntry = true;
region.notifyOnExit = true;
NSLog(#"%d", [CLLocationManager authorizationStatus]);
[[[StoryController sharedController] locationManager] startMonitoringForRegion:region];
I am not receiving any notifications when I leave or enter a 1km range with the app backgrounded.
How do I make this work? When I log out my monitored regions, I do see the lat & lng are correct.
Two things you need to make sure you're doing.
checking for availability of region monitoring
make sure your locationManager.authorizationStatus == .authorizedAlways
Some systems simply don't support region monitoring, and region monitoring will never work unless your authorizationStatus is .authorizedAlways, only then can it monitor in the background.
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/RegionMonitoring/RegionMonitoring.html
Part one:
I have written the following code to monitor iBeacons. I would like to detect the didEnterRegion and didExitRegion event. However it never happens. Would you be able to take a look at the code and suggest what could be missing?
I use the Apple AirLocate sample code to configure one device as iBeacon and perform the following steps to test my code:
Steps:
compile and execute AirLocate sample code on device B
compile and execute this code on device A
in device B use AirLocate app to configure device as iBeacon choosing the following UUID: "74278BDA-B644-4520-8F0C-720EAF059935"
Results:
state inside message
Expected results:
state inside message
did enter region
Why is that?
Those are my plist entries:
Code:
#import "BeaconMonitoring.h"
#implementation BeaconMonitoring
- (instancetype)init
{
self = [super init];
if (self) {
self.locationManager = [[CLLocationManager alloc] init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.monitoredRegions = [[NSMutableArray alloc] initWithCapacity:10];
}
return self;
}
- (void) startRangingForBeacons{
NSLog(#"in startRangingForBeacons");
[self.locationManager startUpdatingLocation];
[self startMonitoringForRegion:[[NSUUID alloc] initWithUUIDString:#"74278BDA-B644-4520-8F0C-720EAF059935"] :#"b"];
}
- (void) startMonitoringForRegion:(NSUUID*)beaconUUID :(NSString*)regionIdentifier{
/**
Alternatively:
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:#"xxxx"
major:10
minor:20
identifier:#"name"]
**/
// Override point for customization after application launch.
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:regionIdentifier];
beaconRegion.notifyEntryStateOnDisplay = NO;
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
[self.locationManager startMonitoringForRegion:beaconRegion];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
[self.monitoredRegions addObject:beaconRegion];
}
- (void) stopRangingForbeacons{
NSLog(#"in stopRangingForbeacons");
[self.locationManager stopUpdatingLocation];
for (int i=0; i < [self.monitoredRegions count]; i++) {
NSObject * object = [self.monitoredRegions objectAtIndex:i];
if ([object isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion * region = (CLBeaconRegion*)object;
[self.locationManager stopMonitoringForRegion:region];
[self.locationManager stopRangingBeaconsInRegion:region];
}
else{
NSLog(#"Serious error, should never happen!");
}
}
}
#pragma CLLocationManagerDelegate
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
[manager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
[self.locationManager startUpdatingLocation];
NSLog(#"You entered the region.");
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*)region];
[self.locationManager stopUpdatingLocation];
NSDictionary * notificationData = #{ #"value" : #"exitedRegion"};
[[NSNotificationCenter defaultCenter] postNotificationName:#"dataUpdate" object:nil userInfo:notificationData];
NSLog(#"You exited the region.");
// [self sendLocalNotificationWithMessage:#"You exited the region."];
}
- (void) locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
NSLog(#"did determine state");
switch (state) {
case CLRegionStateInside:
NSLog(#"state inside");
break;
case CLRegionStateOutside:
NSLog(#"state outside");
break;
case CLRegionStateUnknown:
NSLog(#"state unknown");
break;
default:
NSLog(#"Default case: Region unknown");
break;
}
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
NSLog(#"Did range %lu beacon in region %#", (unsigned long)[beacons count], region.identifier);
NSString * visibleInformation = [NSString stringWithFormat:#"(%lu)", (unsigned long)[beacons count]];
for (int i=0; i<[beacons count]; i++) {
CLBeacon *beacon = [beacons objectAtIndex:i];
if ([beacons count] == 1) {
NSNumber * distance = [NSNumber numberWithFloat:beacon.accuracy];
visibleInformation = [NSString stringWithFormat:#"%i-%i is %f", beacon.major.intValue, beacon.minor.intValue, distance.doubleValue];
}
else{
visibleInformation = [visibleInformation stringByAppendingString:[NSString stringWithFormat:#" %i-%i ", beacon.major.intValue, beacon.minor.intValue]];
}
}
}
#end
Part two:
I had a look at the AirLocate source code to understand if there was something that I had to trigger in the state inside message to get the monitoring working properly. However I found that the ** didDetermineState** method is implemented in the AppDelegate.
Why is that?
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
/*
A user can transition in or out of a region while the application is not running. When this happens CoreLocation will launch the application momentarily, call this delegate method and we will let the user know via a local notification.
*/
UILocalNotification *notification = [[UILocalNotification alloc] init];
if(state == CLRegionStateInside)
{
notification.alertBody = NSLocalizedString(#"You're inside the region", #"");
}
else if(state == CLRegionStateOutside)
{
notification.alertBody = NSLocalizedString(#"You're outside the region", #"");
}
else
{
return;
}
/*
If the application is in the foreground, it will get a callback to application:didReceiveLocalNotification:.
If it's not, iOS will display the notification to the user.
*/
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
Assumption: R = Region made by B and listened to by A
Case 1
IF A was started before B:
app A should tell you determine state for region R = outside
app A should run didEnter Region R
case 2
IF A was infact started after B:
it should only run determineState Region R = inside
the end. There is no 2 here because it never enters the range. it was started inside of it
I have 3 iBeacons which are placed in 3 different rooms. When I walk into each of rooms I'd like to receive a notification while my app is closed that tells me which room I'm in.
My beacons all have the UUID but different major and minor versions.
This is what we've implemented so far in our class (not in App Delegate)
-(void)locationManager:(CLLocationManager*)manager didRangeBeacons:(NSArray*)beacons inRegion:(CLBeaconRegion*)region {
// firstBeacon is the closest beacon
CLBeacon *firstBeacon = [beacons firstObject];
NSLog(#" Major %# Minor %#", firstBeacon.major, firstBeacon.minor);
int major = [firstBeacon.major intValue];
int minor = [firstBeacon.minor intValue];
if (major == 43005 && minor == 52679) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
self.beaconColour.text = #"Green";
}
else if (major == 48891 && minor == 47852) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Light Blue";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
self.beaconColour.text = #"Light Blue";
}
else if (major == 59510 && minor == 42953) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Dark Blue";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
self.beaconColour.text = #"Dark Blue";
}
self.major.text = [NSString stringWithFormat:#"%d", major];
self.minor.text = [NSString stringWithFormat:#"%d", minor];
}
Updated code based on answer
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"com.accenture.testregion"];
self.myBeaconRegion.notifyEntryStateOnDisplay = YES;
self.myBeaconRegion.notifyOnEntry = YES;
self.myBeaconRegion.notifyOnExit = YES;
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
}
- (void)didReceiveMemoryWarnins {
[super didReceiveMemoryWarning];
}
BOOL _isInsideRegion;
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"didEnterRegion");
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
int major = [beaconRegion.major intValue];
int minor = [beaconRegion.minor intValue];
NSLog(#" Major %d Minor %d", major, minor);
if (major == 43005 && minor == 52679) {
self.beaconColour.text = #"Green";
if (!_isInsideRegion) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
_isInsideRegion = YES;
}
else if (major == 48891 && minor == 47852) {
self.beaconColour.text = #"Light Blue";
if (!_isInsideRegion) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
_isInsideRegion = YES;
}
else if (major == 59510 && minor == 42953) {
self.beaconColour.text = #"Dark Blue";
if (!_isInsideRegion) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
_isInsideRegion = YES;
}
}
}
Try not to get first object from beacons array, you should enumerate it and compare which one is which, sometimes you can receive a signal from few beacons.
I made it work by calling didEnterRegion: delegate instead on didRangeBeacons:
You should also create a boolean flag (variable) to prevent duplicate sending of notification. And mark it true inside your if condition when minor/major match and set it to false in didExitRegion:
BOOL _isInsideRegion;
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
int major = [beaconRegion.major intValue];
int minor = [beaconRegion.major.minor intValue];
if (major == 43005 && minor == 52679) {
if (!_isInsideRegion) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = #"Default";
notification.alertBody = #"Green";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
_isInsideRegion = YES;
self.beaconColour.text = #"Green";
}
else if (major == 48891 && minor == 47852) {
if (!_isInsideRegion) {....
}
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
if (major == 43005 && minor == 52679) {
if (!_isInsideRegion) {
// You can send another notification to inform user that he left the region
}
_isInsideRegion = NO;
}
When your app is in the background, you don't get region entry/exit events super quickly. It can take up to 15 minutes to get a notification. See here: http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html
FIXED: I was creating my CLLocationManager in the viewDidLoad method, so it was being cleaned by ARC almost immediately. I changed my instance to be a class instance instead of method instance and the problem is solved.
I have a class, an NSObject, which I'm using to control the entering and exiting of beacon regions.
It's implementation is here:
#import "dcBeaconManager.h"
#implementation dcBeaconManager
-(id)init
{
self = [super init];
if (self != nil){}
return self;
}
bool testRanging = true;
bool firstRegionEntered = true;
- (void)initBeaconManager {
NSLog(#"initBeaconManager called");
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"digiConsRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
- (void)stopBeaconManager {
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
NSLog(#"Started looking for regions");
[self.locationManager requestStateForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"Region discovered");
if (firstRegionEntered) {
NSLog(#"First time in region");
firstRegionEntered = false;
}
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"Region left");
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"We hope you enjoyed the event, thank you for coming.";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
NSLog(#"locationManager initiated");
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
//Store some information about this beacon
NSNumber *currentBeaconMajor = beacon.major; //it's major (group) number
NSNumber *currentBeaconMinor = beacon.minor; //it's minor (individual) number
if (([currentBeaconMinor floatValue] == 59204) && ([currentBeaconMajor floatValue] == 33995) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Mint discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateMint" object:nil];
} else if (([currentBeaconMinor floatValue] == 7451) && ([currentBeaconMajor floatValue] == 63627) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Blue discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateBlue" object:nil];
} else if (([currentBeaconMinor floatValue] == 51657) && ([currentBeaconMajor floatValue] == 26976) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Purple discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocatePurple" object:nil];
}
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (testRanging) {
NSLog(#"Testing: forced ranging");
if ([region isEqual:self.beaconRegion] && state == CLRegionStateInside) {
[_locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
}
#end
Here's the header:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface dcBeaconManager : NSObject <CLLocationManagerDelegate>
//properties
#property (strong, nonatomic) CLBeaconRegion *beaconRegion; //used to define which beacons we are looking for
#property (strong, nonatomic) CLLocationManager *locationManager; //set up location services and allow beacons to be found
//methods
- (void)initBeaconManager;
- (void)stopBeaconManager;
#end
Now, this code has worked fine before when included in the main view controller, but I'm trying to get better at OOP in Obj-C. The logs show the object is created and the main initBeaconManager is called, but from there is just stops. I cannot figure out why. Any thoughts?
Cheers.
I'm not sure this help but can you replace this line:
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"digiConsRegion"];
with that one:
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:1 minor:2 identifier:#"digiConsRegion"];
self.beaconRegion.notifyOnEntry = entry;
self.beaconRegion.notifyOnExit = exit;
self.beaconRegion.notifyEntryStateOnDisplay = YES;
I believe you need to provide major and minor values for bacon region.
I was creating my CLLocationManager in my viewDidLoad method, and ARC was killing it off almost immediately. Moving it to be a class instance instead of a method instance fixed the problem.
I have an odd thing going on.
FIXED: I was creating my CLLocationManager in the viewDidLoad method, so it was being cleaned by ARC almost immediately. I changed my instance to be a class instance instead of method instance and the problem is solved.
UPDATE: The view that is calling the Location Services is shown directly below. The request to allow location services pops up then immediately fades away.
- (void)viewDidLoad
{
[super viewDidLoad];
//Grab the JSON from dcJSONParser
NSURL *mainContentURL = [NSURL URLWithString:#"http://www.andrewlarking.co.uk/DigiCons/appContent.txt"];
dcJSONParser *mainJSONParser = [[dcJSONParser alloc]init];
NSDictionary *mainPageDictonary = [mainJSONParser getContentFromNSURL:mainContentURL];
//Grab the data from a specific JSON collection
NSArray *pageOneContent = mainPageDictonary[#"firstRunPage"];
for ( NSDictionary *pageOne in pageOneContent )
{
//Set the label
self.dcEventDayViewControllerBeaconNameLabel.text = pageOne[#"title"];
}
NSLog(#"Event Day View Loaded");
// Do any additional setup after loading the view.
// Start listening for events from the beacon manager.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFindMint:) name:#"didLocateMint" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFindBlue:) name:#"didLocateBlue" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFindPurple:) name:#"didLocatePurple" object:nil];
//Set Up the beacon manager
dcBeaconManager *beaconManager = [[dcBeaconManager alloc]init];
[beaconManager initBeaconManager];
}
I'm using CLLocationServices but my app is not requesting permission to use Location Services. It appears in the list, and is off by default. I've read that permissions get stored on the device which may explain it as I've tested this app before, but testing on a new device gives the same result.
I don't use location services straight away, the app goes through a few checks and measures before deciding to call a view that uses location services. When it does, this is the code:
-(void)initBeacons {
dcBeaconManager *beaconManager = [[dcBeaconManager alloc]init]; //Create an instance of the dcBeaconManager class.
[beaconManager initBeaconManager]; //Start the beacon manager running.
}
The class that calls is:
#import "dcBeaconManager.h"
#implementation dcBeaconManager
-(id)init
{
self = [super init];
if (self != nil){}
return self;
}
bool testRanging = true;
bool firstRegionEntered = true;
- (void)initBeaconManager {
NSLog(#"initBeaconManager called");
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"digiConsRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
- (void)stopBeaconManager {
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
NSLog(#"Started looking for regions");
[self.locationManager requestStateForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"Region discovered");
if (firstRegionEntered) {
NSLog(#"First time in region");
firstRegionEntered = false;
}
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"Region left");
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"We hope you enjoyed the event, thank you for coming.";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
NSLog(#"locationManager initiated");
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
//Store some information about this beacon
NSNumber *currentBeaconMajor = beacon.major; //it's major (group) number
NSNumber *currentBeaconMinor = beacon.minor; //it's minor (individual) number
if (([currentBeaconMinor floatValue] == 59204) && ([currentBeaconMajor floatValue] == 33995) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Mint discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateMint" object:nil];
} else if (([currentBeaconMinor floatValue] == 7451) && ([currentBeaconMajor floatValue] == 63627) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Blue discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocateBlue" object:nil];
} else if (([currentBeaconMinor floatValue] == 51657) && ([currentBeaconMajor floatValue] == 26976) && (beacon.proximity == CLProximityNear)) {
NSLog(#"Purple discovered");
[[NSNotificationCenter defaultCenter] postNotificationName:#"didLocatePurple" object:nil];
}
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (testRanging) {
NSLog(#"Testing: forced ranging");
if ([region isEqual:self.beaconRegion] && state == CLRegionStateInside) {
[_locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
}
#end
None of the delegate methods get called, Malloc wondered about the Location Services initially so I've looked around and found the above peculiar error.
Any thoughts? I'm happy to share the entire project.
Cheers.