How to solve the issue with beacon? - ios

My Problem: When I click the button, my phone will show a progress view(ask me come close the beacon), and when I come close the beacon, my phone will show a view(checkmark), and then remove the view 1.5s later. Now the view can show normally first, but when I take my phone far away from the beacon, and the view(show checkmark) will show 1s later. It's a big problem out of my mind.
My code:
onClick button
- (IBAction)chooseBLEAction:(id)sender {
self.locationManager = [CLLocationManager new];
[self _initBeaconRegion];
if (([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse ||
[CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways ||
[CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) &&
[self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[self _start];
if (self.bluetoothIsOpen) {
[self _createProgressView];
}
}else {
[self.locationManager requestWhenInUseAuthorization];
}
}
- (void)_start {
[self.locationManager startRangingBeaconsInRegion:self.targetBeaconRegion];
}
- (void)_stop {
[self.locationManager stopRangingBeaconsInRegion:self.targetBeaconRegion];
}
CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray<CLBeacon *> *)beacons inRegion:(CLBeaconRegion *)region {
NSLog(#"searching for beacon !");
CLBeacon *foundBeacon = [CLBeacon new];
foundBeacon = [beacons firstObject];
if (foundBeacon) {
NSLog(#"found beacon !");
switch (foundBeacon.proximity) {
case CLProximityUnknown:
break;
case CLProximityFar:
break;
case CLProximityNear:
break;
case CLProximityImmediate:
{
[self _stop];
NSLog(#"Immediate beacon !");
AudioServicesPlaySystemSound(1051);
[self.myProgressView removeFromSuperview];
[self _createSuceessProgressView];
self.removeProgressViewTimer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(_removeSuccessProgressView) userInfo:nil repeats:NO];
break;
}
}
}
}
create progress view (close the beacon)
- (void)_createProgressView {
NSString *title = #"Please close the beacon";
self.myProgressView = [MRProgressOverlayView showOverlayAddedTo:self.view title:title mode:MRProgressOverlayViewModeIndeterminate animated:YES];
}
create success view
- (void)_createSuceessProgressView {
NSString *title = #"Success!";
self.suceessProgressView = [MRProgressOverlayView showOverlayAddedTo:self.view title:title mode:MRProgressOverlayViewModeCheckmark animated:YES];
}
remove success view 1.5s later
- (void)_removeSuccessProgressView {
[self.removeProgressViewTimer invalidate];
[self.suceessProgressView removeFromSuperview];
}

Related

Inconsistent behaviour by proximity API - iOS iBeacon

I am converting an iOS device into iBeacon and then using core location' ranging API to detect beacon' proximity. Now, this works but I see an unpredictable behaviour where sometimes at a defined distance (say 10 meters away) I see No Beacon sometimes Far Beacon and some times Near Beacon.
Is there a way to make this behaviour more consistent?
- (void)viewDidLoad {
[super viewDidLoad];
self.beaconSwitch.on = NO;
self.beaconView.backgroundColor = [UIColor clearColor];
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
BeaconRegion *beaconRegion = [[BeaconRegion alloc] init];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconRegion.uuid major:[beaconRegion.major shortValue] minor:[beaconRegion.minor shortValue] identifier:beaconIdentifier];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.peripheralManager stopAdvertising];
}
- (IBAction)doneButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)beaconSwitchPressed:(id)sender {
if (self.beaconSwitch.on == true) {
self.beaconView.beaconState = BNBeaconStateBroadcasting;
NSDictionary *data = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
[self.peripheralManager startAdvertising:data];
} else {
self.beaconView.beaconState = BNBeaconStateStop;
[self.peripheralManager stopAdvertising];
}
}
Here is how I am converting my iOS device into a beacon:
And here is how I am getting its proximity:
- (void)viewDidLoad {
[super viewDidLoad];
BeaconRegion *beaconRegion = [[BeaconRegion alloc] init];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconRegion.uuid identifier:beaconIdentifier];
self.beaconRegion.notifyOnEntry = YES;
self.beaconRegion.notifyOnExit = YES;
self.beaconRegion.notifyEntryStateOnDisplay = YES;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.detectionSwitch.on = NO;
}
- (IBAction)beaconDetectionSwitchPressed:(id)sender {
if (self.detectionSwitch.isOn) {
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager requestStateForRegion:self.beaconRegion];
} else {
self.lastProximity = CLProximityUnknown;
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
self.titleLabel.text = #"";
self.messageLabel.text = #"";
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
self.locationManager = nil;
}
- (void)locationManager:(CLLocationManager *)iManager didEnterRegion:(CLRegion *)iRegion {
NSLog(#"Detected a beacon");
if (![self.beaconRegion isEqual:iRegion]) {
return;
}
NSLog(#"Entered into the beacon region = %#", iRegion);
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
NSLog(#"Ranging beacon successful");
if (beacons.count > 0) {
CLBeacon *nearestBeacon = beacons[0];
CLProximity currentProximity = nearestBeacon.proximity;
if (currentProximity == self.lastProximity) {
NSLog(#"No Change in Beacon distance");
} else {
self.lastProximity = currentProximity;
[self updateUserAboutProximity];
}
} else {
self.lastProximity = CLProximityUnknown;
}
}
- (void)updateUserAboutProximity {
NSString *title = #"--";
NSString *message = #"--";
switch (self.lastProximity) {
case CLProximityUnknown:{
title = #"No Beacon";
message = #"There is no nearby beacon";
}
break;
case CLProximityImmediate:{
title = #"Immediate Beacon";
message = #"You are standing immediate to video wall!";
}
break;
case CLProximityNear:{
if (self.isServerCallOn) {
title = #"Near Beacon";
message = #"You are standing near to video wall!";
} else {
title = #"Near Beacon";
message = #"You are standing near to video wall! Initiating a server call.";
if (!self.ignoreSeverCallTrigger) {
self.isServerCallOn = YES;
[self talkToServer];
} else {
NSLog(#"Ignoring server call trigger");
message = #"Ignoring server call trigger!";
}
}
}
break;
case CLProximityFar:{
title = #"Far Beacon";
message = #"You are standing far from video wall!";
}
break;
default:
break;
}
self.titleLabel.text = title;
self.messageLabel.text = message;
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (state == CLRegionStateInside) {
NSLog(#"Starting Ranging");
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (![CLLocationManager locationServicesEnabled]) {
NSLog(#"Location Service Not Enabled");
}
if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways) {
NSLog(#"Location Service Not Authorized");
}
}
For now, I have put a condition on received signal strength indicator (rssi) value and accepting anything > -60 in Near proximity to make the distance little more predictable. So, within Nearby proximity also I trigger my action if rssi > -60.

Understanding iBeacons in iOS: didDetermineState and didEnterRegion events

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

startUpdatingLocation is working only when wifi is ON but not when only 3G in ON

i have tried to get the longitude/latitude of the user current location, my code is simply this :
MyLocationManager.h
#interface MyLocationManager : NSObject
+(MyLocationManager*)sharedInstance;
-(void)checkAndStartLocationManager;
-(void)stopUpdatingLocation;
#end
MyLocationManager.m
+ (MyLocationManager*)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
MyLocationManager *instance = _sharedInstance;
instance.locationManager = [CLLocationManager new];
instance.locationManager.delegate = instance;
instance.locationManager.distanceFilter = 100;
instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
instance.locationManager.pausesLocationUpdatesAutomatically = YES;
instance.locationManager.activityType = CLActivityTypeOtherNavigation;
});
return _sharedInstance;
}
-(void)checkAndStartLocationManager
{
switch([CLLocationManager authorizationStatus])
{
case kCLAuthorizationStatusAuthorizedWhenInUse:
[self startUpdatingLocation];
break;
case kCLAuthorizationStatusNotDetermined:
if ([self.locationManager respondsToSelector:
#selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}
}
break;
case kCLAuthorizationStatusRestricted:
break;
case kCLAuthorizationStatusDenied:
break;
case kCLAuthorizationStatusAuthorizedAlways:
[self startUpdatingLocation];
break;
default:
break;
}
}
-(void)startUpdatingLocation
{
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch([CLLocationManager authorizationStatus])
{
case kCLAuthorizationStatusAuthorizedWhenInUse:
[self startUpdatingLocation];
break;
case kCLAuthorizationStatusNotDetermined:
break;
case kCLAuthorizationStatusRestricted:
break;
case kCLAuthorizationStatusDenied:
break;
case kCLAuthorizationStatusAuthorizedAlways:
[self startUpdatingLocation];
break;
default:
break;
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations: (NSArray *)locations
{
CLLocation *location = [locations lastObject];
NSString*locationInfo = [NSString stringWithFormat:#"lat - %f, lon - %f", location.coordinate.latitude, location.coordinate.longitude];
NSLog(#"Location is %#", locationInfo);
[self.locationManager stopUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError: (NSError *)error
{
NSLog(#"didFailWithError: %#", error);
}
I am using ios 8 and tested on iphone 5 device. Whenever i call,
[[MyLocationManager sharedInstance] checkAndStartLocationManager];
If wifi on phone is ON then only
didUpdateLocations
is getting called.
If wifi is OFF and only 3G in ON then i always received error as,
Error Domain=kCLErrorDomain Code=0 "The operation couldn’t be completed. (kCLErrorDomain error 0.)"
Please help !!!
Device uses WiFi to fast determine location by crowdsourcing hotspots despite of result may be inaccurate. When you disable WiFi, the only remaining possibility is use GPS module. However, if you are in some building or GPS signal is not strength enough, it will take more time to determine your location and you will get that messages for a while

iPhone4S strange behavior at Beacon detection

I'm developing app that includes ibeacon detection.
But, when device receiving beacon A after B in background, nothing happened.
Condition
1. iPhone4S(iPhone5 is all ok)
2. App is in background
3. After detection of another beacon(different BeaconRegion from another one).
Can someone help me? Any suggestion would be appreciated.
Thank you for replying.
Entering the area of Beacon A(second one) is delayed about 30 seconds from Entering the area of Beacon B(first one), and I waited about 20 seconds for LocalNotification that will be fired by "Beacon A".
(Beacon-A area and Beacon-B area overlap each other partially. And I waited in the overlapped area.)
This is piece of code.
- (void)startBeaconMonitoring
{
if ([CLLocationManager respondsToSelector:#selector(isMonitoringAvailableForClass:)] && [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] && !self.locationManager) {
self.locationManager = [CLLocationManager new];
self.locationManager.delegate = self;
_storeUUID = [[NSUUID alloc] initWithUUIDString:#"MY UUID HERE"];
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:_storeUUID major:CENSOR_TYPE_A identifier:#"MY ID1"];
region.notifyOnExit = YES;
region.notifyOnEntry = YES;
CLBeaconRegion *region2 = [[CLBeaconRegion alloc] initWithProximityUUID:_storeUUID major:CENSOR_TYPE_B identifier:#"MY ID2"];
region.notifyOnExit = YES;
region.notifyOnEntry = YES;
[self.locationManager startMonitoringForRegion:region];
[self.locationManager startMonitoringForRegion:region2];
}
}
# pragma CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
[self.locationManager requestStateForRegion:region];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) {
CLBeaconRegion *beacon = (CLBeaconRegion*)region;
[self.locationManager startRangingBeaconsInRegion:beacon];
}
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
switch (state) {
case CLRegionStateInside:
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) {
CLBeaconRegion *beacon = (CLBeaconRegion*)region;
int major = [beacon.major intValue];
[self.locationManager startRangingBeaconsInRegion:beacon];
}
break;
case CLRegionStateOutside:
case CLRegionStateUnknown:
default:
break;
}
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) {
[self.locationManager stopRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusAuthorized:
if (_locationDisabled) {
_locationDisabled = NO;
self.locationManager = nil;
[self startBeaconMonitoring];
}
break;
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusNotDetermined:
break;
case kCLAuthorizationStatusDenied:
_locationDisabled = YES;
break;
default:
break;
}
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
if (beacons.count > 0 && !_regionExit) {
for (CLBeacon *beacon in beacons) {
if ([beacon.proximityUUID.UUIDString isEqualToString:_storeUUID.UUIDString]) {
NSString *censorType = [NSString stringWithFormat:#"%#", beacon.major];
if ([censorType intValue] == CENSOR_TYPE_A) {
// DO ACTION A
} else if ([censorType intValue] == CENSOR_TYPE_B) {
// DO ACTION B
}
}
}
}
}
It looks that iPhone4s on iOS 8 has problem with detecting quick region change in background. My test showed that if you loose first region for more than 1 min and enter new region than it is detected immediately. But if you loose region for less than 1 min iPhone 4s won't recognise it and you will have to wait (around 15 min) for full bluetooth scan to notice it. I made workaround that I start ranging after losing first region for 60 seconds, if I will enter next beacon in 60 seconds ranging will detect it.

CLRegionState is UnKnown When i"m in the region

I'm testing iBeacon, I've used didDetermineState: method and the CLRegionState is CLRegionStateUnknown when I'm inside the region, can anybody tell me why?
My Bluetooth is On and detecting the beacons when I range them for CLBeaconStateUnknown, sender and receiver are in range (1 Meter), here is my Implementation
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
switch (state) {
case CLRegionStateInside:
currentBeconRegion = (CLBeaconRegion *)region;
[self.locationManager startRangingBeaconsInRegion:currentBeconRegion];
NSLog(#"INSIDE the Region");//not logging
break;
case CLRegionStateOutside:
NSLog(#"OUTSIDE the Region");
break;
case CLRegionStateUnknown:
default:
NSLog(#"Region state UNKNOWN!!!"); //Logging on console
statusLabel.text = #"Region state UNKNOWN!!!";
//[self.locationManager stopRangingBeaconsInRegion:beaconRegion];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];//Suceessfully ranging the beacon
break;
}
}
You say you are "inside the region" but how do you know the iBeacon is really being detected? Is Bluetooth turned on and working? Has the user allowed location access when prompted?
I would enable iBeacon ranging and log anything detected to verify your device really is seeing the iBeacon. I suspect you will either find you do not detect anything, or perhaps the iBeacon.proximity field will be returning CLProximityUnknown due to a bad calibration.
The didDetermineState gets an extra call even if no beacons have ever been detected when you start up monitoring (or if you awake the screen when notifyEntryStateOnDisplay=YES is set on you region). This is the only case where I would think you might get a CLRegionStateUnknown value. I have never seen this value returned, so I am curious under what conditions it happens. Finding the answers to my questions above may solve this. Please report back!
I've restarted both of my devices and reinstalled the app, still running into the same problem and also its not firing didEnterRegion: and didExitRegion:.
- (void)viewDidLoad
{
[super viewDidLoad];
//set peripheralManager delegate
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
queue:nil
options:nil];
//Set location managaer delegate
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
uuid = [[NSUUID alloc] initWithUUIDString:#"E01D235B-A5DB-4717-8D2F-28D5B48931F1"];
// Setup a new region with that UUID and same identifier as the broadcasting beacon
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"testRegion"];
self.beaconRegion.notifyEntryStateOnDisplay = YES;
self.beaconRegion.notifyOnEntry = YES;
self.beaconRegion.notifyOnExit = YES;
}
#pragma mark -
#pragma mark - Peripheral Manager Delegates
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
[self.locationManager startMonitoringForRegion:self.beaconRegion];
break;
case CBPeripheralManagerStatePoweredOff:
statusLabel.text = #"Bluetooth is Off";
[self clearFileds];
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
break;
case CBPeripheralManagerStateUnknown:
default:
statusLabel.text = #"Bluetooth is Unsupported";
[self clearFileds];
[self.locationManager stopMonitoringForRegion:self.beaconRegion];
break;
}
}
#pragma mark -
#pragma mark - Location Manager Delecate Methods
-(void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
[self.locationManager requestStateForRegion:self.beaconRegion];
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
switch (state) {
case CLRegionStateInside:
currentBeconRegion = (CLBeaconRegion *)region;
[self.locationManager startRangingBeaconsInRegion:currentBeconRegion];
NSLog(#"INSIDE the Region");
break;
case CLRegionStateOutside:
NSLog(#"OUTSIDE the Region");
break;
case CLRegionStateUnknown:
default:
NSLog(#"Region state UNKNOWN!!!");
statusLabel.text = #"Region state UNKNOWN!!!";
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
break;
}
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
//check if we're ranging the correct beacon
if (![self.beaconRegion isEqual:region]) { return;}
NSLog(#"didEnterRegion");
if([region isKindOfClass:[CLBeaconRegion class]])
{
currentBeconRegion = (CLBeaconRegion *)region;
if ([beaconRegion.identifier isEqualToString:#"testRegion"])
{
//send local notificaation
UILocalNotification *notice = [[UILocalNotification alloc] init];
notice.alertBody = #"Becoan Found!!!";
notice.alertAction =#"View";
[[UIApplication sharedApplication] presentLocalNotificationNow:notice];
//srart raning beacon region
[self.locationManager startRangingBeaconsInRegion:currentBeconRegion];
}
}
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
if ([beacons count] > 0) {
int major = [beacons.firstObject major].intValue;
int minor = [beacons.firstObject minor].intValue;
if (major != self.foundBeacon.major.intValue || minor != self.foundBeacon.minor.intValue) {
NSLog(#"Found Beacon is: %#", [beacons firstObject]);
}
self.foundBeacon = [[CLBeacon alloc] init];
self.foundBeacon = [beacons firstObject];
//Set the ui
[self setFileds];
}
else //[beacon count] 0
{
statusLabel.text = #"No becaons found";
[self clearsFileds];
// [self.locationManager stopMonitoringForRegion:beaconRegion];
}
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(#"didExitRegion");
if([region isKindOfClass:[CLBeaconRegion class]])
{
currentBeconRegion = (CLBeaconRegion *)region;
if ([beaconRegion.identifier isEqualToString:#"testRegion"])
{
[self.locationManager stopMonitoringForRegion:currentBeconRegion];
}
}
}
This post was written based on iOS 8 this may change in the future:
I'm posting an answer here in case someone else is having the same problem as me. I spent almost 4 hours on this in total before realizing that it was because my phone was in airplane mode.
tl;dr
If airplane mode is on, region notifications don't come through
I had the same problem before, and the answer was that I've simply forgotten to call:
[self.locationManager startMonitoringForRegion:beaconRegion];
I assumed wrongly at first that if you start ranging with:
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
... and getting data for rssi, accuracy and UUID in locationManager:didRangeBeacons:inRegion: method, the CLLocationManager should be able to figure out that the phone was in that region, but apparently not. So when asking for state, with:
[self.locationManager requestStateForRegion:beaconRegion];
I got CLRegionStateUnknown for that region in the locationManager:didDetermineState:forRegion: like you did.

Resources