Using multiples Beacons (poping a view on each different beacon) - ios

I m a beginner in objective C.
My app work correctly with one beacon.
I'm using the "estimote SDK".
I have many problems, i want to use 2 or 3 beacons. I want to push a View for each of the beacons.
I don't understand how i can do it with multiple beacons.
I don't know if i have to use multiple beacon manager. (ESTBeaconManager* beaconManager)
I dont know how to pass differents regions to the didRangeBeacons:(NSArray *)beacons inRegion:(ESTBeaconRegion *)region
Can i use one beacon only for notification and the 2 others to pop 2 differents view when i m close of them. ( one different view for each beacon )
Thanks for your help.
Best Regards.
Code:
#import "ESTViewController.h"
#import "PresentViewController.h"
#import <ESTBeaconManager.h>
#import <AudioToolbox/AudioToolbox.h>
#interface ESTViewController () <ESTBeaconManagerDelegate>
#property (nonatomic, strong) ESTBeaconManager* beaconManager;
#property (nonatomic, strong) ESTBeaconManager* beaconManager2;
#property (nonatomic, strong) ESTBeaconManager* beaconManager3;
#property (nonatomic, strong) ESTBeacon* selectedBeacon;
#end
#implementation ESTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// should i create one manager instance or more ?
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
self.beaconManager.avoidUnknownStateBeacons = NO;
//self.beaconManager2 = [[ESTBeaconManager alloc] init];
//self.beaconManager2.delegate = self;
//self.beaconManager2.avoidUnknownStateBeacons = NO;
//self.beaconManager3 = [[ESTBeaconManager alloc] init];
//self.beaconManager3.delegate = self;
//self.beaconManager3.avoidUnknownStateBeacons = NO;
// My Differents regions
region = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
major:12800 minor:228 identifier:#"Icy Marshmellow"];
region2 = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
major:12800 minor:128 identifier:#"Mint Cocktail"];
region3 = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
major:12800 minor:328 identifier:#"Blueberry Pie"];
// Should i do it for each region with one ESTBeaconManager or 3 ?
[self.beaconManager requestStateForRegion:region];
[self.beaconManager requestStateForRegion:region2];
[self.beaconManager requestStateForRegion:region3];
}
// NOTIFICATION METHOD :
-(void)beaconManager:(ESTBeaconManager *)manager
didEnterRegion:(ESTBeaconRegion *)region
{
// iPhone/iPad entered beacon zone
// present local notification
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Hello blabla blabla";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
-(void)beaconManager:(ESTBeaconManager *)manager
didExitRegion:(ESTBeaconRegion *)region
{
// iPhone/iPad left beacon zone
// present local notification
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"bye bye";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.beaconManager startRangingBeaconsInRegion:region];
[self.beaconManager startMonitoringForRegion:region];
//[self.beaconManager2 startRangingBeaconsInRegion:region2];
//[self.beaconManager2 startMonitoringForRegion:region2];
//[self.beaconManager3 startRangingBeaconsInRegion:region3];
//[self.beaconManager3 startMonitoringForRegion:region3];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.beaconManager stopRangingBeaconsInRegion:region];
[self.beaconManager stopMonitoringForRegion:region];
//[self.beaconManager2 stopRangingBeaconsInRegion:region2];
//[self.beaconManager2 stopMonitoringForRegion:region2];
//[self.beaconManager3 stopRangingBeaconsInRegion:region3];
//[self.beaconManager3 stopMonitoringForRegion:region3];
}
// My problem is here , i dont know how i can pass differents regions here
-(void)beaconManager:(ESTBeaconManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(ESTBeaconRegion *)region
{
if([beacons count] > 0)
{
if(!self.selectedBeacon)
{
// initialy pick closest beacon
self.selectedBeacon = [beacons objectAtIndex:0];
}
else
{
for (ESTBeacon* cBeacon in beacons)
{
// update beacon it same as selected initially
if([self.selectedBeacon.major unsignedShortValue] == [cBeacon.major unsignedShortValue] &&
[self.selectedBeacon.minor unsignedShortValue] == [cBeacon.minor unsignedShortValue])
{
self.selectedBeacon = cBeacon;
}
}
}
switch (self.selectedBeacon.proximity)
{
case CLProximityUnknown:
{
self.rangeStatusImageView.image = [UIImage imageNamed:#"logo_signal.jpg"];
self.descriptionStateLabel.text = #"Signal lost";
break;
}
case CLProximityImmediate:
{
[self performSegueWithIdentifier: #"presentSegue" sender: self];
break;
}
case CLProximityNear:
{
self.rangeStatusImageView.image = [UIImage imageNamed:#"logo_near_bleu.jpg"];
self.descriptionStateLabel.text = #"Come closer";
break;
}
case CLProximityFar:
{
self.rangeStatusImageView.image = [UIImage imageNamed:#"logo_far_clair.jpg"];
self.descriptionStateLabel.text = #"Welcome";
break;
}
default:
break;
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
EDIT
Ok i worked on my code and now i do it with one region. my array of beacons have 3 beacons.
region = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID identifier:#"multibeacons"];
i dont use major or minor at the init.
in ViewDidAppears i do :
[self.beaconManager startRangingBeaconsInRegion:region];
The delegate like this :
-(void)beaconManager:(ESTBeaconManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(ESTBeaconRegion *)region
{
// I used a sort , sorting by distance
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// if breakpoint here 3 beacons in array
self.beaconsArray = [beacons sortedArrayUsingDescriptors:sortDescriptors];
if([self.beaconsArray count] > 0)
{
if(!self.selectedBeacon)
{
// initialy pick closest beacon
self.selectedBeacon = [beacons objectAtIndex:0];
currentBeaconMinor = self.selectedBeacon.minor;
}
else
{
for (ESTBeacon* cBeacon in self.beaconsArray)
{
// update beacon it same as selected initially
if([self.selectedBeacon.major unsignedShortValue] == [cBeacon.major unsignedShortValue] &&
[self.selectedBeacon.minor unsignedShortValue] == [cBeacon.minor unsignedShortValue])
{
self.selectedBeacon = cBeacon;
currentBeaconMinor = self.selectedBeacon.minor;
}
}
}
I sort by distance and i have a currentBeaconMinor Value. My array of beacon have 3 beacons inside if i put a breakpoint i can see 3.
In the Switch proximity , i have do like this :
switch (self.selectedBeacon.proximity)
{
case CLProximityImmediate:
{
if ([currentBeaconMinor floatValue] == 128)
{
NSLog(#"128 128 128");
//[self performSegueWithIdentifier: #"presentSegue1" sender: self];
}
else if ([currentBeaconMinor floatValue] == 228)
{
NSLog(#"228 228 228");
//[self performSegueWithIdentifier: #"presentSegue2" sender: self];
}
else if ([currentBeaconMinor floatValue] == 328)
{
NSLog(#"328 328 328");
//[self performSegueWithIdentifier: #"presentSegue3" sender: self];
}
break;
}
But that still dont work :((( I m getting mad. My app pick initialy the closest beacon. After that app always keep the same beacon and never change. I move the beacon near the device but nslog always send me the same minor number. Please can you give me some help ? I m sure i m doing something wrong.

Each beacon has 3 pieces of information - a UUID, a major number and a minor number. When you create a beacon region you must specify the UUID as a minimum. You can optionally specify a major and minor value. There is a limit to the number of beacon regions that iOS will scan for in the background (I believe it is 20), so generally it is best to be as broad as possible in your region registration and then determine when notified if you are interested in the beacon that is visible.
All Estimote beacons have the same UUID, so if you register a region with just the UUID your app will be notified whenever you are in range of any Estimote beacon. I can see that your three beacons are all using major 12800, so you could simply create a region that specified the UUID and the major and then you would be notified whenever a beacon with those values was visible - or you can do as you have done and register specific regions for each of your three beacons.
You only need a single EstBeaconManager instance to manage all regions.
Whenever you enter or exit a region your didEnterRegion and didExitRegion methods will be called. Also your didRangeBeacons method will be called if you are currently ranging beacons.
In these delegate methods you need to examine the major & minor (well, really just the minor, because in your case the major is always the same) values to determine which action you want to take. You can also examine the region identifier string to determine which beacon is visible.
Note that more than one beacon may be in range, so you may need to examine the proximities to determine which action you want to take.
Finally, although you define the three regions, you are not ranging/monitoring them all, so change your viewWillAppear to
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.beaconManager startRangingBeaconsInRegion:region];
[self.beaconManager startMonitoringForRegion:region];
[self.beaconManager startRangingBeaconsInRegion:region2];
[self.beaconManager startMonitoringForRegion:region2];
[self.beaconManager startRangingBeaconsInRegion:region3];
[self.beaconManager startMonitoringForRegion:region3];
}

I haven't used the Estimote SDK before. I just treat my Estimote beacons as vanilla iBeacons and use Apple's Location Manager framework.
It looks to me like you are very confused.
I looked at the Estimote SDK briefly, and it looked quite similar to Apple's location manager.
In the location manager, you create a beacon region, then you ask the location manager to start ranging for that beacon region (which tells you about estimated distance readings or you use startMonitoringForRegion to ask to be notified about enter/exit region events.
You can do both at the same time. Glancing at the Estimate APIs, it looks like they use the same approach.
You need to create a beacon region, then call startRangingBeaconsInRegion and/or startMonitoringForRegion to ask for range and/or enter/exit region notifications.
You then wait for your delegate method(s) to be called when a beacon enters/exits range, (startMonitoringForRegion) or changes distance (startRangingBeaconsInRegion)
Looking at the docs, it seems that calling requestStateForRegion will cause the Estimote beacon manager to call your locationManager:didDetermineState:forRegion: delegate method once and only once.
In your code, you haven't asked to monitor for regions or to range beacons, so all 3 regions will return a "not in range" state.
Don't call those requestStateForRegion. Call startMonitoringForRegion and/or startRangingBeaconsInRegion, and wait for the system to notify you when the beacon status changes.

Related

Region Monitoring (Geofencing) drains battery (iOS)

I've implemented in my app the region monitoring feature of CLLocationManager, it works but it kills my battery:
-
-
Is it should be like that?
My code:
monitorLocationViewController.m (please scroll to see the full code):
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
//If "allStores"(NSMutableArray) isn't nil - calling "locationChangeHandler" to update monitoring
if (self.allStores!=nil) {
[self locationChangeHandler];
}
CLLocation *currentLocation=(CLLocation*)[locations lastObject];
NSSet *monitoredRegionsSet=self.locationManager.monitoredRegions;
[monitoredRegionsSet enumerateObjectsUsingBlock:^(CLCircularRegion *region, BOOL *stop) {
if ([region containsCoordinate:currentLocation.coordinate]) {
[self.locationManager stopMonitoringForRegion:region];
[self locationManager:self.locationManager didEnterRegion:region];
}
}];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
Store *store=[self storeForRegion:region];
if (store.alreadySendNotification==NO) {
UILocalNotification *notification=[[UILocalNotification alloc] init];
notification.alertTitle=#"Arounder";
notification.alertBody=[[self storeForRegion:region] address];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
store.alreadySendNotification=YES;
}
}
//For updating monitoring
-(void)locationChangeHandler
{
//If "allStores"(NSMutableArray) isn't nil
if (self.allStores!=nil) {
//Finding the 20 closest stores to he user's location and adding it to "twentyClosestStores"(NSMutableArray)
[self sortClosestStores];
//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location updated)
[self stopMonitoringStores];
//Start monitoring "twentyClosestStores"(NSMutableArray)
[self startMonitoringClosestStores];
}
}
//Start monitoring "twentyClosestStores"(NSMutableArray)
-(void)startMonitoringClosestStores
{
//If monitoring isn't availible for "CLCircularRegion"
if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
NSLog(#"Monitoring is not available for CLCircularRegion class");
return;
}
//Run on all "twentyClosestStores"(NSMutableArray)'s objects
for (Store *currentStore in self.twentyClosestStores) {
//Start monitoring "region"(CLCircularRegion)
[self.locationManager startMonitoringForRegion:currentStore.circularRegion];
}
}
//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location updated)
-(void)stopMonitoringStores
{
//Run on all "monitoredRegions"(NSSet) of "locationManager"(CLLocationManager) objects
for (CLCircularRegion *currentRegion in self.locationManager.monitoredRegions) {
//Stop monitoring "region"(CLCircularRegion)
[self.locationManager stopMonitoringForRegion:currentRegion];
}
}
//Finding a store for region
-(Store*)storeForRegion:(CLCircularRegion*)region
{
//Run on all "allStores"(NSMutableArray)'s objects
for (Store *currentStore in self.allStores) {
//If "currentStore"(Store)'s "circularRegion"'s identifier is equal to "region"(CLCircularRegion)'s identifier
if ([currentStore.circularRegion.identifier isEqualToString:region.identifier]) {
//Returning "currentStore"(Store)
return currentStore;
}
}
//Store not found - returning nil
NSLog(#"No store found for this region: %f,%f",region.center.latitude,region.center.longitude);
return nil;
}
AppDelegate.m:
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.monitorLocationVC=[[monitorLocationViewController alloc] init];
self.monitorLocationVC.locationManager=self.locationManager;
[self configureLocationManager];
[self.locationManager startUpdatingLocation];
return YES;
}
-(void)configureLocationManager
{
//Initializing locationManager
self.locationManager=[[CLLocationManager alloc] init];
//setting "locationManager"'s(CLLocationManager) delegate to "self"
self.locationManager.delegate=self.monitorLocationVC;
//Setting "locationManager"'s(CLLocationManager)'s distance filter to none
self.locationManager.distanceFilter=kCLDistanceFilterNone;
//Setting "locationManager"'s(CLLocationManager)'s activityType to navigation
self.locationManager.activityType=CLActivityTypeAutomotiveNavigation;
//setting "locationManager"'s(CLLocationManager) desiredAccuracy to "best"
self.locationManager.desiredAccuracy=kCLLocationAccuracyBestForNavigation;
self.locationManager.pausesLocationUpdatesAutomatically=NO;
//If OS version is 9 or above - setting "allowsBackgroundLocationUpdates" to YES
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
}
Thank you!
You only want to monitor regions, not update their location constantly in the background.
Try this:
self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;
Do you really need the distanceFilter set to kCLDistanceFilterNone? That will cause more battery power to be used. You probably want to try to set that to around 10, 20, 50 or even 100 meters.
Also, in order to not update locations constantly, instead of:
[self.locationManager startUpdatingLocation];
Try just using:
[self.locationManager startMonitoringSignificantLocationChanges];
All of these things should contribute to less battery usage. When you set accuracy and distance filters to the highest possible setting, the battery is going to be drained.
EDIT:
You are going to eat up a lot of battery whatever you do because of the purpose of your app. A solution I've done before with a problem similar to this is to create algorithm or formula with an NSTimer that fires every x minutes to update the user's location. (but only update regions if they have moved x meters).
stop location updates between firing of the NSTimer so that you aren't constantly updating locations.
when the timer fires, resume location updates, grab about 10 locations (so you get an accurate one), then shut off location updates until the next time the timer is fired

CLLocationManager location monitoring not working properly

I have an app that will notify the user every time he approaches to one of my client's stores. There are more than 20 stores, so I have a function that takes the user's location and finds the 20 nearest stores to him and start monitoring the location of these stores, every time the user moves, the app finds the 20 nearest stores again, removes the previous stores from monitoring and start monitoring the new ones.
For some reason, it doesn't work, I'll be happy if one of you (or more :)) will help me to find the problem, Thanks!!
myCode (scroll to see the full code):
Note: the CLLocationManager created on the AppDelegate.m and it's delegate is this class (UIViewController).
-(void)sortClosestStores
{
[self.allStores sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
CLLocation *location1=[[CLLocation alloc] initWithLatitude:((Store*)obj1).geoPoint.latitude longitude:((Store*)obj1).geoPoint.longitude];
CLLocation *location2=[[CLLocation alloc] initWithLatitude:((Store*)obj2).geoPoint.latitude longitude:((Store*)obj2).geoPoint.longitude];
float dist1 =[location1 distanceFromLocation:self.locationManager.location];
float dist2 = [location2 distanceFromLocation:self.locationManager.location];
if (dist1 == dist2) {
return NSOrderedSame;
}
else if (dist1 < dist2) {
return NSOrderedAscending;
}
else {
return NSOrderedDescending;
}
}];
if (self.twentyClosestStores==nil) {
self.twentyClosestStores=[NSMutableArray array];
}
if (self.previousTwentyStores==nil) {
self.previousTwentyStores=[NSMutableArray array];
}
self.previousTwentyStores=self.twentyClosestStores;
self.twentyClosestStores=[NSMutableArray array];
for (int i = 0; i < 20; i++) {
[self.twentyClosestStores addObject:[self.allStores objectAtIndex:i]];
}
}
-(void)startMonitoringClosestStores
{
if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
NSLog(#"Monitoring is not available for CLCircularRegion class");
}
for (Store *currentStore in self.twentyClosestStores) {
CLCircularRegion *region=[currentStore createCircularRegion];
[self.locationManager startMonitoringForRegion:region];
}
}
-(void)stopMonitoringStores
{
for (Store *currentStore in self.previousTwentyStores) {
CLCircularRegion *region=[currentStore createCircularRegion];
[self.locationManager stopMonitoringForRegion:region];
}
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
if (self.allStores!=nil) {
[self sortClosestStores];
[self stopMonitoringStores];
[self startMonitoringClosestStores];
}
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"Entered"); //Not called even when the user enters one of the regions.
}
Can you please help me? Thanks!
I'm pretty new at CoreLocation myself but I would think that it is not a good idea to call stopMonitoringForRegions and startMonitoringForRegions in didUpdateLocations.
Since you're monitoring regions, the didEnterRegion delegate is what you will be interested in. That will give you the 'hey, I arrived at the X store' event, and in there is where you would probably want to call the code that you currently have in your didUpdateLocations.
You will want to setup CoreLocation probably in your AppDelegate, so you might have something like (sorry about it being Swift, that's what I'm working in right now):
locationManager.delegate = self
// auths:
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
// config:
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.allowsBackgroundLocationUpdates = true
locationManager.activityType = CLActivityType.AutomotiveNavigation
// start:
locationManager.startMonitoringSignificantLocationChanges()
Then you would have your code:
if (self.allStores!=nil) {
[self sortClosestStores];
[self stopMonitoringStores];
[self startMonitoringClosestStores];
}
Note: I don't think it matters if you call startMonitoringSignificantLocationChanges before or after adding monitored regions, I haven't gotten quite that far in my code yet.
didUpdateLocations is more for when you want to track location e.g. tracking a bicycle ride or jogging session.
Additional explanation:
OK, I think I understand the issue now. There are two aspects to what you want to accomplish:
being notified when the user enters a store's region
dynamically recalculating the nearest N stores as the device moves
My previous answer was geared towards the first issue.
Regarding the second issue, dynamically recalulating nearest N, the code in your didUpdateLocations will not be called unless you tell the location manager to startUpdatingLocation. Just off the top of my head:
// configure the location manager in AppDelegate:
// You will need to experiment with these two properties,
// you probably don't want to use kCLLocationAccuracyBestForNavigation.
// Maybe kCLLocationAccuracyKilometer would be sufficient.
locmgr.distanceFilter = n
locmgr.desiredAccuracy = m
locmgr.pausesLocationUpdatesAutomatically = true
locmgr.startUpdatingLocation()
// this is the delegate that will receive the events due to locmgr.startUpdatingLocation
locationManager:didUpdateLocation {
// Unless you have a specific need for it, I would refactor so that
// you don't need self.PreviousTwentyStores:
[self stopMonitoringStores];
[self sortClosestStores];
[self startMonitoringClosestStores];
}
locationManager:didEnterRegion {
// you are in the region surrounding one of the stores.
}
Alternately, consider just setting a timer and waking the app every N seconds or minutes to recalculate the nearest stores.
As I understand the various aspects of CoreLocation
startUpdatingLocation -> didUpdateLocations -> stopUpdatingLocation
is (in one sense) entirely separate from:
startMonitoringForRegion -> didEnterRegion | didExitRegion -> stopMonitoringForRegion
Additionally, startUpdatingLocation was never called so your didUpdateLocation was never called.

Assertion Failure with Estimote

I'm having some problems when I try and run my "xcode" project and I get this "run time" error.
Assertion failure in -[CLLocationManager startRangingBeaconsInRegion:], /SourceCache/CoreLocationFramework/CoreLocation-1613.35/Framework/CoreLocation/CLLocationManager.m:991
2014-05-11 15:47:15.483 Ziew[1516:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: region != nil'
I'm trying use the "Estimote SDK" to build a similar application using the proximity application code example. The original example works great and I didn't change anything when I added their code. Here some of my methods:
- (id)initWithBeacon:(ESTBeacon *)beacon
{
self = [super init];
if (self)
{
self.beacon = beacon;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor colorWithRed:(141/255.0) green:(198/255.0) blue:(63/255.0) alpha:1]];
// Do any additional setup after loading the view.
//Beacon Manager setup
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
self.beaconRegion = [[ESTBeaconRegion alloc] initWithProximityUUID:self.beacon.proximityUUID
major:[self.beacon.major unsignedIntValue]
minor:[self.beacon.minor unsignedIntValue]
identifier:#"RegionIdentifier"];
[self.beaconManager startRangingBeaconsInRegion:self.beaconRegion];
}
#pragma mark - ESTBeaconManager delegate
- (void)beaconManager:(ESTBeaconManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(ESTBeaconRegion *)region
{
if (beacons.count > 0)
{
ESTBeacon *firstBeacon = [beacons firstObject];
[self textForProximity:firstBeacon.proximity];
}
NSLog(#"No beacons within region");
}
#pragma mark -
- (void)textForProximity:(CLProximity)proximity
{
switch (proximity) {
case CLProximityFar:
NSLog(#"Far");
break;
case CLProximityNear:
NSLog(#"Near");
break;
case CLProximityImmediate:
NSLog(#"Immediate");
break;
default:
NSLog(#"Unknown");
break;
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[self.beaconManager stopRangingBeaconsInRegion:self.beaconRegion];
[super viewDidDisappear:animated];
}
What am I missing?
SOLUTION
Thank you to everyone who responded. Using the Estimote SDK you can assign a constant UUID with the beacon corresponding major and minor id to get the startRangingBeaconsInRegion method called.
self.beaconRegion = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
major:your_major_id
minor:your_minor_id
identifier:#"RegionIdentifier"];
From what I can tell your beacon region is nil by the time you tell it to start ranging beacons. Since you assign to your property an initialized instance of a region, maybe your property declaration is listed as weak instead of strong?
You have probably copied initWithBeacon constructor from their demos and this constructor is not being called when view controller is being instantiated (like from xib or storyboard).
Because self.beacon is nil, constructed ESTRegion object is allocated with nil params which causes crash.
You need to:
range for beacons around you first
or
hardcode beacon identifiers in your app (or fetch it from somewhere)

Give a time between tasks - iOS

I'm getting my location position from the iPhone's GPS. I want to get the coordinate from the same point 15 times (to get the best horizontal accuracy).
Is there a way to wait for example, 2 seconds between one coordinate and another?
I use an object called coordinate, with latitude and longitude as property.
.... Exemple code
Coordinate * coord = [[Coordinate alloc] init];
NSMutableArray *coordinates = [[NSMutableArray alloc] init];
for (i=0 ; i<=14; i++)
{
coord = newlocation;
[coordinates addObject:coord];
.... some code to wait 2 seconds before add a new object to the array....
}
I tried to use NSThread sleepfortimeinterval, but the view freezes.
Thanks
Rather than having a for loop like this, you could theoretically use a repeating NSTimer that fired every two seconds, and then invalidate the timer after 15 iterations.
But I'd not suggest doing that, but rather shift to an event-driven model, waiting for calls to your didUpdateLocations. There's no point in checking in two seconds, if didUpdateLocations hasn't been updated. Likewise, there's no point in repeatedly checking 15 times over 30 seconds if, for example, you get a really accurate location after 5 seconds.
I'd suggest start monitoring the location, watch the locations as they come in with subsequent calls to didUpdateLocations, and examine horizontalAccuracy of the CLLocation (which tells you how accurate the location is). Once you reach the desired horizontalAccuracy, you can declare success (e.g. stop monitoring locations or whatever). You could also establish a NSTimer that automatically turned off the monitoring of locations after 30 seconds, if you want, as well.
For example:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%s", __PRETTY_FUNCTION__);
[self startStandardUpdates];
// after 30 seconds, if we haven't found a location, declare success with whatever we got (if anything)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self stopStandardUpdates]; // stop monitoring location if you want
if (!self.foundLocation) {
if (self.bestLocation) {
NSLog(#"Didn't find perfect location, but location has accuracy of %.1f meters", self.bestLocation.horizontalAccuracy);
} else {
NSLog(#"Even after 30 seconds, did not find any locations!");
}
}
});
}
#pragma mark - Location Services
- (void)startStandardUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Set a movement threshold for new events.
self.locationManager.distanceFilter = 5;
[self.locationManager startUpdatingLocation];
}
- (void)stopStandardUpdates
{
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation* location = [locations lastObject];
NSLog(#"%s: horizontalAccuracy = %.1f", __FUNCTION__, location.horizontalAccuracy);
if (location.horizontalAccuracy < 0) // not a valid location
return;
// this checks to see if the location is more accurate than the last;
// or you might just want to eliminate this `if` clause, because if
// you get updated location, you can probably assume it's better than
// the last one (esp if the user might be moving)
if (!self.bestLocation || location.horizontalAccuracy <= self.bestLocation.horizontalAccuracy) {
self.bestLocation = location;
}
if (location.horizontalAccuracy <= 5) { // use whatever you want here
NSLog(#"Found location %#", location);
self.foundLocation = YES;
[self stopStandardUpdates]; // stop it if you want
}
}
This uses the following properties:
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLLocation *bestLocation;
#property (nonatomic) BOOL foundLocation;

Can't detect when iBeacon bluetooth is turned off iOS

I'm writing two simple apps. One is a beacon app whose signal you can start or stop with the touch of a button. The other is a receiver app that edits the text of a label when it detects the signal of a beacon.
I've tried using the didDetermineStateForRegion, didExitRegion, and didEnterRegion methods to detect when an app is running. These work fine for determining when the receiver moves in and out of proximity of the beacon, but it takes roughly 30 seconds to determine that I've turned the bluetooth on the beacon off. I've also tried setting my CLLocationManager's pausesLocationUpdatesAutomatically field to NO, but same thing. Ideally, it'd just immediately put "No" to my label; how do I do this?
MyView.h
#interface MyView : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *statusLabel;
#property (strong, nonatomic) CLBeaconRegion *myBeaconRegion;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
MyView.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Initialize location manager and set ourselves as the delegate
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically=NO;
// Create a NSUUID with the same UUID as the broadcasting beacon
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"ID"];
// Setup a new region with that UUID and same identifier as the broadcasting beacon
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"identifier"];
// Tell location manager to start monitoring for the beacon region
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
}
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion*)region
{
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
self.statusLabel.text = #"Yes";
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (state == CLRegionStateInside) {
self.statusLabel.text = #"Yes";
} else {
self.statusLabel.text = #"No";
}
}
-(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion*)region
{
[self.locationManager stopRangingBeaconsInRegion:self.myBeaconRegion];
self.statusLabel.text = #"No";
}
Region monitoring is pretty slow, you can use it for more general notification to let you know when you are near an iBeacon. I think for this case you would want to use didRangeBeacons, where your detection app will be notified of the beacon signal strength every second. You can use this signal strength to decide if you no longer see the beacon (CoreLocation tends to still "see" the beacon a few seconds after it disapears).
Just add the following method to your delegate:
-(void)locationManager:(CLLocationManager*)manager
didRangeBeacons:(NSArray*)beacons
inRegion:(CLBeaconRegion*)region
And start ranging for your region with:
[locationManager startRangingBeaconsInRegion:(CLBeaconRegion*)region];

Resources