CLLocationManager + monitoring for CLBeaconRegion - ios

I'm trying to make applications, which ranges CLBeaconRegion.
I watched video from WWDC and presenter said, that I should call startMonitoringForRegion and, then user is inside region, startRangingBeaconsInRegion. I tried.
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
CLBeaconRegion *targetRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:UUIDString] identifier:identifier];
targetRegion.notifyEntryStateOnDisplay = YES;
targetRegion.notifyOnEntry = YES;
targetRegion.notifyOnExit = YES;
[_locationManager startMonitoringForRegion:targetRegion];
[_locationManager startUpdatingLocation];
But it's not sending nothing to delegate. Beacons is working.
If I call just
[_locationManager startRangingBeaconsInRegion:region];
Applications finds all my beacons around me.
Should I just call second method or I'm incorrect?
Have you any suggestions?

The most likely explanation is that you are not waiting long enough to get a call to your delegate. When you are NOT ranging, it takes up to 15 minutes to get callbacks to the delegate methods below. When you are ranging, it takes only 1 second.
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
If you wait the 15 minutes, I suspect you will get the callbacks as expected. The reason it is so much faster when you are ranging is because iOS does constant bluetooth scans when ranging is enabled to look for iBeacons. When you are not ranging, it slows down these scans to save battery. See more info in the blog posts below:
http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html
http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html

Related

How to get location on each 200 meters in terminated state in ios

i am trying to get user's location in app's terminated state. i am doing this by startMonitoringSignificantLocationChanges but it's giving location after 3km or after 5 min. so can't create a route properly. please guide me how to do this.
if (_anotherLocationManager)
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
self.anotherLocationManager = [[CLLocationManager alloc]init];
_anotherLocationManager.delegate = self;
_anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
I solved my query myself...
Create a locationManager object and alloc it like this
self.locationManager = [[CLLocationManager alloc]init]; // initializing locationManager
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; // setting the accuracy
[self.locationManager requestAlwaysAuthorization];
if([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
[self.locationManager setAllowsBackgroundLocationUpdates: YES];
}
self.locationManager.distanceFilter = 20.0;
[self.locationManager startMonitoringSignificantLocationChanges];
self.locationManager.activityType=CLActivityTypeAutomotiveNavigation;
[self.locationManager startUpdatingLocation];
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.delegate = self;
Now set location manager delegate method.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
{
if (newLocation.speed>7){
// here you got location, i settled speed 7 for my convenience.
}
if (newLocation.horizontalAccuracy <= self.locationManager.desiredAccuracy) {
//Desired location Found
[self.locationManager stopUpdatingLocation ];
}
}
You must have to write this stopUpdatingLocation, else your battery consumption will increase so high.
Use the Geofencing to achieve the same.
Create a Geofence of 200mtr radius.
By default, notifyOnExit would be true
Implement the delegate didExitRegion of LocationManager
For termination state, the app would be lunched with UIApplication.LaunchOptionsKey.location in didFinishLaunchingWithOptions.
Create an instance of Location Manager object on location launch key, you obtain the region at which the location exited and keep creating the 200mtr fence on every exit.
You're not going to get 200 meter granularity or continuous tracking with significant location monitoring.
Have you seen these notes in the docs:
The significant-change location service delivers updates only when
there has been a significant change in the device’s location, such as
500 meters or more.
If GPS-level accuracy isn’t critical for your app and you don’t need
continuous tracking, you can use the significant-change location
service.

iBeacon and iOS 9

I'm currently working on app that requires iBeacon monitoring.
I wrote the app one year ago, using iOS 8.x SDK.
It was working as it was supposed to, but now, one year from then, the same code doesn't work anymore (I'm testing it with the same beacons!).
Beacon regions detection has become much more unpredictable.
It has a will of its own.
Some beacons get detected, some are just ignored.
I couldn't find anything relevant on OpenRadar.
A few people complained about something similar on Apple Dev Forums, but Apple never came back to them.
Thoughts?
This is how I initialize the location manager.
self.locationManager = [CLLocationManager new];
self.locationManager.delegate = self;
// Worst accuracy is set in order to preserve battery life.
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
self.locationManager.allowsBackgroundLocationUpdates = YES;
// Required to keep the app living in the background.
// Background mode "Location Updates" is enabled.
[self.locationManager startUpdatingLocation];
I think before you were using geofences or CLCircular regions. Core location doesn't need any of that code to detect iBeacons. Try setting it up like this:
self.locationManager = [CLLocationManager new];
self.locationManager.delegate = self;
[self.locationManager startMonitoringForRegion:beaconRegion]; // Where "beaconRegion" is a CLBeaconRegion with a UUID that matches the beacon you want to detect (major & minor optional)
beaconRegion.notifyEntryStateOnDisplay = YES;
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
You also need to add NSLocationAlwaysUsageDescription to your info.plist. Once all that is done, you should begin getting enter and exit events for your beacons through these two methods:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region;

iBeacon monitoring not working properly

When there is a beacon in range of iOS device, I get notified(CLRegionStateInside) and can start ranging. This works properly. However, when ranging is started and the the iOS devices is not in range anymore, I don't get notified(State doesn't change to CLRegionStateOutside). Not in foreground or background.
Also didEnterRegion and didExitRegion never gets called. I start ranging in didDeterminState when state is CLRegionStateInside.
I do have background refresh settings enabled for my app.
When I start the app for the first time I do get an alert asking for location permission.
So basically, i'm able to start ranging, but i'm not able to stop ranging, because the state doesn't change to CLRegionStateOutside.
I use Xcode 6.3.1, iOS 8.3 on iPhone 4s.
This is my code:
INIT:
- (id)init{
self = [super init];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization]; //or requestWhenInUseAuthorization
}
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"B75FA2E9-3D02-480A-B05E-0C79DBB922AD"];
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"TESTREGIO"];
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
[self.locationManager requestStateForRegion:self.myBeaconRegion];
self.myBeaconRegion.notifyEntryStateOnDisplay = YES;
self.myBeaconRegion.notifyOnEntry = YES;
self.myBeaconRegion.notifyOnExit = YES;
return self;
}
DETERMINESTATE:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (state == CLRegionStateInside)
{
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
}else{
[self.locationManager stopRangingBeaconsInRegion:self.myBeaconRegion];
}
}
DIDENTERREGION and DIDEXITREGION:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion*)region
{
NSLog(#"DidenterRegion================================================");
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"DidexitRegion================================================");
}
You are not going to get a didExitRegion notification unless you were already inside a region. I suggest you track the state of the region your are looking for separately.
I don't understand what you are doing in didDetermineState. You started ranging for your beacon in init(). You would not need to start ranging again.
A few tips:
You do not need to request any background permissions for this to work.
calls to didExitRegion happen 3 seconds after a beacon is no longer detected when the device is ranging in the foreground. In the background, or in the foreground when not ranging, these calls can be delayed for up to 15 minutes. These delays always exist on iPhone 4S models. On newer models, the delays may or may not exist depending on whether there are enough hardware assist slots for detecting beacons. See details here: http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html
I suspect the failure to get exit events is really an issue of them taking longer than expected. Wait 15 minutes and see if the callbacks come.
You should also call the following function to start ranging
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];

CLLocationManager didEnterRegion: with iBeacon while app is suspended

I'm trying to wake up my app (relaunch it) when it enters my defined beacon region but I just can't get it to work. This are the steps and code I'm using.
Set "Location updates" Background Mode to YES.
Monitor my CLBeaconRegion
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"EBEFD083-70A2-47C8-9837-E7B5634DF524"];
beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"daRegion"];
beaconRegion.notifyEntryStateOnDisplay = NO;
beaconRegion.notifyOnEntry = YES;
beaconRegion.notifyOnExit = YES;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringForRegion:beaconRegion];
Implement delegate methods
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region;
Anything that I might be missing? I have read the documentation, blog posts, forums and nothing seems to work. This is one of the websites I read, and this is the other.
The comment "I just can't get it working when the app is killed" is critical.
If you use the iOS7 app switcher to kill an app (e.g. by swiping up on the app icon), then you will not be able to re-launch the app in the background upon entering or leaving an iBeacon region. This is by design -- if the user doesn't want the app running, then Apple thinks code should not be able to make it re-launch. See this thread.
Fortunately, users don't typically do this. For testing purposes, if you want to completely stop an app, don't do this. Reboot your phone instead. (Note, however, that it takes a minute or so after boot before you can detect iBeacons.)
EDIT 2014/03/10: This behavior has changed as of the release of iOS 7.1. Killing an app from the task switcher no longer stops it from detecting iBeacons in the background.

Best technique for counting laps?

I'm making an app that needs to count laps for participants walking around a track. I thought that I could create a small geo-fence around the start point and have the OS let me know when the user entered it, however this doesn't seem to work as well as I was hoping. It seems that it isn't triggered reliably when the walker enters the fence or triggers in other areas around the track. (I'm testing without letting the device go to sleep at this time).
It also seems that the locationManager:didExitRegion method isn't called properly in my testing.
When the user taps the start button, the following method is called:
-(void)startLocationManager {
if ([CLLocationManager locationServicesEnabled] && usingLocationManager) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLLocationAccuracyBest;
self.locationManager.activityType = CLActivityTypeFitness;
[self.locationManager startUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *lastLocation = [locations lastObject];
if (lastLocation.horizontalAccuracy > 10) {
return;
}
// setup a geocode fence and count as user enters fence
self.startRegion = [[CLCircularRegion alloc] initWithCenter:lastLocation.coordinate radius:10 identifier:#"startPosition"];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringForRegion:self.startRegion];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
lapCount++;
[self updateLapDisplay];
}
Am I doing something wrong or can I not use CoreLocation in such a fashion?
A geofence won't work well for this. You can't rely on it to trigger accurately or reliably. Use a location manager and its delegate in the normal way and trigger manually when the user comes to within 10 or 20 meters, then reset the trigger when they're 50 or 100 meters away from it.

Resources