How does ibeacon work on background? - ios

I want to create a order when someone enters into the iBeacon region on app
background,but I have a problem when the app on background.
I know if user open "location" and "bluetooth" and enter into region, app will detect the ibeacon.But after entering into region, user open "bluetooth",the app can't receive the entry notification(sometime work) and can't invoke the function
locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region so the order can't be created.
Does anyone have experience on it?

For more realistic and accurately get bluetooth state value, override its delegate to controller. Core bluetooth framework will help here to get desire accuracy.
Add <CoreBluetooth/CoreBluetooth.h> framework in project.
Use method
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
to check bluetooth is powered on?
Peripheral Manager State will gives you more details about peripheral state and related delegates.
So when ever device has turn on it's bluetooth, above delegate method will be called.
And here you can manually start to check for region updates.
For Exmaple:
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDelegate:self];
_beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:<Identifier>];
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBPeripheralStateConnected) {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
}
The method startRangingBeaconsInRegion will invoke the method:
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region;

Related

Objective c: iBeacon ranging doesn't fire didEnterRegion

I´ve implemented a beacon region monitoring in my AppDelegate.
I test my code in foreground.
The didRangeBeacons event is thrown successfully and regularly. (I see the amount of beacons and also the count change in the didRangeBeacons if i turn off my beacon.)
My didEnterRegion event was not fired.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//....
self->beaconLocationManager = [[CLLocationManager alloc] init];
self->beaconLocationManager.delegate = self;//(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSUUID *uuid=[[NSUUID alloc]initWithUUIDString:#"200F0B26-2215-1518-442B-5F0616412530"];
clBeconRegion=[[CLBeaconRegion alloc]initWithProximityUUID:uuid identifier:#"BluetoothExample"];
clBeconRegion.notifyOnEntry=YES;
[self->beaconLocationManager requestAlwaysAuthorization];
[self->beaconLocationManager startMonitoringForRegion:clBeconRegion];
[self->beaconLocationManager startRangingBeaconsInRegion:clBeconRegion];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"ENTER REGION");//not fired
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
NSLog(#"beacons list %# ",beacons);//successfully fired regularly
}
I also enabled Background App Refresh in general settings.
Info.plist entry:
<key>NSLocationAlwaysUsageDescription</key><string>bla bla bla</string>
<key>NSLocationWhenInUseUsageDescription</key><string>bla bla bla</string>
The monitoringDidFailForRegion writes the following error:
CLBeaconRegion (identifier:'BluetoothExample', uuid:200F0B26-2215-1518-442B-5F0616412530, major:(null), minor:(null)) The operation couldn’t be completed. (kCLErrorDomain error 4.)
You need to enable BLE background permission as well.
Go to Project Target >> Background Modes >> Capabilities check Uses Bluetooth LE Accessories. Look on this Image
I found my problem.
It is an existing app and I granted the location in a fewer version only for "in usage".
Now I need "always".
I deleted an reinstalled the app and it works. (only update doesn't help)

iOS stops finding beacons when locking the screen

I write an app that uses beacons and everything is working fine when the app is in the foreground. However when I press the power button and dim the screen the app is not finding any beacons anymore. The app is still hitting the:
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)clBeacons inRegion:(CLBeaconRegion *)region
method, but the logger tells me what 0 beacons has been found, which is weird, because due to the documentation, this methods should be called only when there are any beacons in range:
Tells the delegate that one or more beacons are in range.
I already have kCLAuthorizationStatusAuthorizedAlways permission from user (it's added to .plist file as well) and I have added these capabilities to the project:
This is my code for ranging beacons:
- (instancetype)init {
self = [super init];
if (self) {
NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:[Registry environment].config.beaconUuid];
self.region = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID identifier:proximityUUID.UUIDString];
self.locationManager = [CLLocationManager new];
self.locationManager.delegate = self;
}
return self;
}
- (void)stopMonitoringBeacons {
[self.locationManager stopMonitoringForRegion:self.region];
DDLogInfo(#"Stopped monitoring beacons.");
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[manager requestStateForRegion:region];
[self startRangingBeacons];
}
- (void)startRangingBeacons {
[self.locationManager startRangingBeaconsInRegion:self.region];
DDLogInfo(#"Started ranging beacons.");
}
- (void)locationManager:(CLLocationManager *)manager rangingBeaconsDidFailForRegion:(CLBeaconRegion *)region withError:(NSError *)error {
DDLogInfo(#"Beacon ranging failed with error: %#.", error.localizedDescription);
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)clBeacons inRegion:(CLBeaconRegion *)region {
DDLogDebug(#"Found %tu beacons.", clBeacons.count);
// call the server (no worries, not all the time)
}
Any idea what am I missing? Or maybe I can't trust these logs?
SOLVED:
The problem was the way I've tested it. As #davidgyoung mentioned, ranging works only in foreground and if we want to run it in background we need to start it from monitoring, which informs us about new beacons in background. I already did it in my code:
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[manager requestStateForRegion:region];
[self startRangingBeacons];
}
But this method is called only if we meet new beacon. I was already simulating beacon when my app was in foreground, so when I dimmed the screen with power button, ranging was stopped and monitoring was never called - I would have to turn the beacon off/on or walk out/in of the beacon range to call this method again.
Beacon ranging on iOS generally only works in the foreground, and for about 10 seconds after your app first moves to the background. Beacon monitoring on the other hand can work in the background and wake your app up when a beacon is detected. If you do both ranging and monitoring simultaneously, you will again get 10 seconds of background ranging after a monitoring trigger wakes your app up in the background.
You can extend background ranging from 10 seconds to 3 minutes on request. I wrote a blog post on how to do this.

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];

Registering region with registerRegionWithCircularOverlay Method

I'm able to get my Current Location using CoreLocation framework. I want to register my current location so that i want to know who has entered and exited the region. For that i have used apple documented method to register a region which is - (void)registerRegionWithCircularOverlay:(MKCircle*)overlay andIdentifier:(NSString*)identifier { } . My question is when does this method gets called?
document link : https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/RegionMonitoring/RegionMonitoring.html
Any help?
You must have to take care of permission to access location of user device. So that application is able to track user location.
You can do this by following:
Add a key NSLocationWhenInUseUsageDescription in a .plist file. This is key has a value of type String. You must take care don't use other than String type here.
Ask user to before access to user location, add below line.
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager requestWhenInUseAuthorization];
Below code will start monitoring region:
// Tell location manager to start monitoring for the region
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
Also override below methods to make sure that region is discovered:
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion *)region
{
// We entered a region!
NSLog(#"Entered in region");
}
-(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion *)region
{
// Exited the region
NSLog(#"Exited from region");
}

didEnterRegion only fired on device wake up

I'm using local notifications triggered by iBeacons in my app. It works fine both in foreground and background as long as the iPhone is active, but doesn't trigger didEnterRegion after about 15 minutes of inactivity or reboot.
It then only fires again on wake up of the iPhone with the home button or sleep button, but I want didEnterRegion to "fire" when the iPhone is in a pocket for 15 minutes or more while entering the region as well.
Is this possible? If yes, how?
Background modes > Location updates is disabled
Some code:
.h
#property (strong, nonatomic) CLBeaconRegion *beaconRegion;
#property (strong, nonatomic) CLLocationManager *locationManager;
.m
- (void)start {
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"com.bla.bla"];
self.beaconRegion.notifyOnEntry = YES;
self.beaconRegion.notifyOnExit = YES;
self.beaconRegion.notifyEntryStateOnDisplay = YES;
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
[self.locationManager requestStateForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{
NSLog(#"%#", [error localizedDescription]);
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (state == CLRegionStateInside) {
[manager startRangingBeaconsInRegion:self.beaconRegion];
} else {
[manager stopRangingBeaconsInRegion:self.beaconRegion];
}
}
I'm not sure what is going on here, but the experience described in the question is not consistent with what I have seen testing on multiple devices. Posting the code that sets this up might help figure out some answers.
In several apps, I have been able to get background didEnterRegion callbacks, even after much more than 15 minutes of inactivity without pressing the shoulder button or the home button. And to do this, I have not had to set any background modes. (Apple will actually reject your app if you submit it to the store with background mode Location updates set unnecessarily.)
There is a bug in iOS 7.1 that cam halt iBeacon detections at some point after boot, so perhaps this is what is going on in this case. Details are here. Unfortunately, testing this hypothesis would require you to wake up the screen in order to turn bluetooth off and on to clear the condition, and that would wake up your screen and give you the region exit anyway. Perhaps you could try setting beaconregion.notifyEntryStateOnDisplay=NO, recreating this condition then try cycling bluetooth to see if you then get the notification. You can also use an off the shelf beacon scanning app like Locate for iBeacon to see if your device is able to range for iBeacons at all after it gets into this state, and only cycle to Bluetooth if you are unable to detect iBeacons.

Resources