How to display notifications when multiple iBeacons are in range - ios

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

Related

CLRegion background monitoring not occuring

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

How to notify user when iBeacon range is "Immediate" or "near"?

Users will only get notified when they are close enough to the beacon, since then didEnterRegion dose not work properly.
My code is like this:
if ([region isKindOfClass:[CLBeaconRegion class]] && ([beaconRegionInStringFormat isEqualToString:#"Immediate"] || [beaconRegionInStringFormat isEqualToString:#"Near"]))
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Please open the application";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
But the notification will keep sending to users. How can I only get notified once?
1. Adding a flag to your controller
Interface:
#property (nonatomic) BOOL userNotified;
Implementation:
if (!self.userNotified && [region isKindOfClass:[CLBeaconRegion class]] && ([beaconRegionInStringFormat isEqualToString:#"Immediate"] || [beaconRegionInStringFormat isEqualToString:#"Near"]))
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Please open the application";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
self.userNotified = YES;
}
2. Using the GCD (Grand Central Dispatch)
Using this method the code will be executed only once per app launch.
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
if ([region isKindOfClass:[CLBeaconRegion class]] && ([beaconRegionInStringFormat isEqualToString:#"Immediate"] || [beaconRegionInStringFormat isEqualToString:#"Near"]))
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Please open the application";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
});

How to get paired bluetooth devices

I want to create one application that show me paired devices in my app.(for example any device that paired me before detect and show me.)
also in the next time I want to send one NSString like "hello" to paired device.
I searching in google and I got so confused!!!
Please tell me first how to get paired device with my mobile and second how to send NSString value to them.
Here is an exemple for what you need :
You have to adapt it to match what u want. But you can see how it work ...
#implementation ViewController
{
CBPeripheralManager *_peripheralManager;
BOOL _isAdvertising;
}
- (void)_startAdvertising
{
NSUUID *estimoteUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:estimoteUUID
major:2
minor:1
identifier:#"SimEstimote"];
NSDictionary *beaconPeripheralData = [region peripheralDataWithMeasuredPower:nil];
[_peripheralManager startAdvertising:beaconPeripheralData];
}
- (void)_updateEmitterForDesiredState
{
if (_peripheralManager.state == CBPeripheralManagerStatePoweredOn)
{
// only issue commands when powered on
if (_isAdvertising)
{
if (!_peripheralManager.isAdvertising)
{
[self _startAdvertising];
}
}
else
{
if (_peripheralManager.isAdvertising)
{
[_peripheralManager stopAdvertising];
}
}
}
}
#pragma mark - CBPeripheralManagerDelegate
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
[self _updateEmitterForDesiredState];
}
#pragma mark - Actions
- (IBAction)advertisingSwitch:(UISwitch *)sender
{
_isAdvertising = sender.isOn;
[self _updateEmitterForDesiredState];
}
#end
This for monitoring :
#implementation AppDelegate
{
CLLocationManager *_locationManager;
BOOL _isInsideRegion; // flag to prevent duplicate sending of notification
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options
{
// create a location manager
_locationManager = [[CLLocationManager alloc] init];
// set delegate, not the angle brackets
_locationManager.delegate = self;
NSUUID *estimoteUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:estimoteUUID
identifier:#"Estimote Range"];
// launch app when display is turned on and inside region
region.notifyEntryStateOnDisplay = YES;
if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
{
[_locationManager startMonitoringForRegion:region];
// get status update right away for UI
[_locationManager requestStateForRegion:region];
}
else
{
NSLog(#"This device does not support monitoring beacon regions");
}
// Override point for customization after application launch.
return YES;
}
- (void)_sendEnterLocalNotification
{
if (!_isInsideRegion)
{
UILocalNotification *notice = [[UILocalNotification alloc] init];
notice.alertBody = #"Inside Estimote beacon region!";
notice.alertAction = #"Open";
[[UIApplication sharedApplication] scheduleLocalNotification:notice];
}
_isInsideRegion = YES;
}
- (void)_sendExitLocalNotification
{
if (_isInsideRegion)
{
UILocalNotification *notice = [[UILocalNotification alloc] init];
notice.alertBody = #"Left Estimote beacon region!";
notice.alertAction = #"Open";
[[UIApplication sharedApplication] scheduleLocalNotification:notice];
}
_isInsideRegion = NO;
}
- (void)_updateUIForState:(CLRegionState)state
{
ViewController *vc = (ViewController *)self.window.rootViewController;
if (state == CLRegionStateInside)
{
vc.label.text = #"Inside";
}
else if (state == CLRegionStateOutside)
{
vc.label.text = #"Outside";
}
else
{
vc.label.text = #"Unknown";
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
// always update UI
[self _updateUIForState:state];
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
// don't send any notifications
return;
}
if (state == CLRegionStateInside)
{
[self _sendEnterLocalNotification];
}
else
{
[self _sendExitLocalNotification];
}
}
#end
You can have the complete description at this adress :
http://www.cocoanetics.com/2013/11/can-you-smell-the-ibeacon/

iBeacon ranging and proximity is buggy

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.

No vibration when receiving UILocalnotification

I don't get it. This is my code where I configure my LocalNotification and fired it. It works fine but there is no vibration. I think there should be a vibration when I receive a Notification?
I tried AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)/ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) without no luck.
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.soundName = UILocalNotificationDefaultSoundName;
if(state == CLRegionStateInside)
{
notification.alertBody = #"Bla Bla";
wasWelcomeBefore = TRUE;
}
else if(state == CLRegionStateOutside)
{
notification.alertBody = #"Bla";
}
else
{
NSLog(#"locationManager didDetermineState OTHER for %#", region.identifier);
return;
}
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive){
if (!isNotifiForFirstBeacon && wasWelcomeBefore) {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
NSDictionary *dataDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:isNotifiForFirstBeacon] forKey:#"isFirstBeacon"];
isNotifiForFirstBeacon = TRUE;
notification.userInfo = dataDict;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
}

Resources