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)
Related
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.
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;
It's a further problem based on the question in iBeacon Bluetooth didEnterRegion and didExitRegion methods are never fired which have been solved.
In detail, the method of didEnterRegion and didExitRegion are never fired while the beacon application in the background or the device locked. In addition, these two methods could be triggered normally while the beacon application in the front.
My application is based on the apple demo "Airlocated (sample code, provided by apple inc)" in the bellow link:
https://developer.apple.com/library/ios/samplecode/AirLocate/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013430-Intro-DontLinkElementID_2. I hardly modify any code except adding some code as bellow:
in file "APLAppDelegate.m"
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"Entered region: %#", region);
[self sendLocalNotificationForBeaconRegion:(CLBeaconRegion *)region];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(#"Exited region: %#", region);
}
in file APLMonitoringViewcontroller.m
- (void)updateMonitoredRegion
{
...
[self.locationManager startMonitoringForRegion:region];
[self.locationManager startRangeingForRegion:region];
...
}
Complement a bit. I have tried the bellow methods:
Set "Location updates" Background Mode to YES
specify notifyOnExit and notifyOnEntry as true
reboot iPhone4s with iOS 7.1.2
Could anyone give me some suggestions on it?
How long are you waiting for background detections? Under some circumstances, this can take up to 15 minutes. See here for details.
As described in that article, you do not need to set location updates Background Mode to YES, nor do you need to specify notifyOnExit and notifyOnEntry as true.
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.
I have an app which is set to monitor entry into regions. On entry, the app will alert the user with a local notification.
This works fine when the app is on, or in the background. However if the app is terminated, the region monitoring never raises the local notification.
I have set my "background modes" key in the info.plist.
Could this be because my CLLocation code is not in the AppDelegate (instead it is in a singleton)?
Could this be because it's not possible to run code to raise the location notification from a terminated state?
Here's my code when the region is entered:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
UIApplication* app = [UIApplication sharedApplication];
UILocalNotification* notifyAlarm = [[UILocalNotification alloc] init];
notifyAlarm.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.repeatInterval =NSDayCalendarUnit;
notifyAlarm.alertBody = [Installation currentInstallation].reminderText;
[app scheduleLocalNotification:notifyAlarm];
}
There are 2 things happen:
a) If the application is suspended when an update occurs, the system wakes it up in the background to handle the update.
b) If the application starts this service and is then terminated, the system relaunches the application automatically when a new location becomes available.
What we can now do is turn on significant location updates when the user hits the home key and we can let the system wake us up when needed.
-(void) applicationDidEnterBackground:(UIApplication *) application
{
// You will also want to check if the user would like background location
// tracking and check that you are on a device that supports this feature.
// Also you will want to see if location services are enabled at all.
// All this code is stripped back to the bare bones to show the structure
// of what is needed.
[locationManager startMonitoringSignificantLocationChanges];
}
Then to perhaps switch to higher accuracy when the application is started up, use;
-(void) applicationDidBecomeActive:(UIApplication *) application
{
[locationManager stopMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
}
Next you’ll likely want to change your location manager delegate to handle background location updates.
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
BOOL isInBackground = NO;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
isInBackground = YES;
}
// Handle location updates as normal.
if (isInBackground)
{
// Do, if you have to send location to server, or what you need
}
else
{
// ...
}
}
Please Note: Location monitoring in background will take effect on battery usage.
This code is taken from http://www.mindsizzlers.com/2011/07/ios-background-location/