deffered Location updates are not available - ios

I am trying to fetch user locations in foreground & background. I have to call api after I got a locaion update. To work in background I want to use Deferred method. I followed the same process as described in Apple WWDC. I am checking app on iPhone 5 (iOS 7). It is working fine when I am in foreground but did not give me update after I send the app into background. Below is the code which I am using to get location in background.
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.locationArray = [[NSMutableArray alloc] init];
self.locationErrorArray = [[NSMutableArray alloc] init];
self.manager_loc = [[CLLocationManager alloc] init];
self.manager_loc.activityType = CLActivityTypeFitness;
self.manager_loc.delegate = self;
[self.manager_loc setDesiredAccuracy:kCLLocationAccuracyBest];
[self.manager_loc startUpdatingLocation];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#pragma mark - Location Manager Delgate
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"update failed");
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[self.locationArray addObject:locations];
NSLog(#"udate locations %f %f", manager.location.coordinate.latitude, manager.location.coordinate.longitude);
if (!self.deferredStatus)
{
self.deferredStatus = YES;
[self.manager_loc allowDeferredLocationUpdatesUntilTraveled:100 timeout:30];
}
[self.manager_loc stopUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error
{if (manager.location != nil)
{ [self.locationArray addObject:manager.location];
}
if (error != nil)
{
[self.locationErrorArray addObject:error.description];
}
self.deferredStatus = NO;
NSLog(#"deffered success %f %f", manager.location.coordinate.latitude, manager.location.coordinate.longitude);
}
#end
If I do not stop the location update in didUpdateToLocations Delegate then the location arrow (on status bar) do not go. In that case it gives me locations contionusly. I want location update after a particular time or particualar distance travelled, so that I can hit server with the user locations. Please help me on this.

Use LocationManger distanceFilter Property for update location at particualar distance travelled.
self.manager_loc.distanceFilter= 100;// In meters

If you want location updated in Backggriound then register your for background updates. Youca can do it in plist.
Set location manager to :
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)]) {
self.locationManager.pausesLocationUpdatesAutomatically = NO;
}
Then If you want to location updated after some time or distance then use:
- (void)allowDeferredLocationUpdatesUntilTraveled:(CLLocationDistance)distance
timeout:(NSTimeInterval)timeout // No guaranty it will work exactly or not
If you want location updated based on distance the you can use
Desired accuracty and distanceFilter property.
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;// Use other accurarcy as your need
self.locationManager.distanceFilter = 3000; //100, 200, etc
If you set activity type to CLActivityTypeFitness all above setting will overrided, And location manager updated according to this activity, which is as per my knowledge will eat Battery.
While using CLLocation Manager one thing you should accept it will not give all updartes 100% accurate.

See my answer for this post: StartUpdateLocations in Background, didUpdatingToLocation only called 10-20 times
If you need location updates in the background under iOS 7, you must call startUpdatingLocation while your App is in the foreground. You can no longer do this while your App is in the background, so you can no longer register for location updates only when you need them and while you need them. You are forced to register for them for the whole time your App is running (in the foreground and the background) and so you’re forced to waste a lot of energy.
You can reduce the battery usage a little bit by setting the accuracy to kCLLocationAccuracyThreeKilometers when you do not need the location updates and set them to kCLLocationAccuracyBest only when you need the updates. But this will nevertheless drain the battery faster than expected.
Please write a bug report to Apple and ask for the „old" behavior of iOS 4,5 and 6, where you could call „startUpdatingLocation“ in the background as well to get location updates in the background. If Apple gets enough requests to change this behavior back to the way it was implemented in iOS 5/6, the more likely it is that Apple will change this back.
The currents situation is really bad. Bad for developers, which are forced to waste energy, or to abandon their Apps, bad for the user, whose device needs to be plugged to a power source much earlier, or who can no longer use certain Apps.

Related

How to track location consistently in the background on iOS?

We implemented a functionality that tracks the user location on the background at least every 15 minutes. At the moment the background tracking works great for a couple of hours, some cases up until 8 hours, and other cases can go up to 6 days.
The issue is that at a certain point while the user is stationary the app stops managing the location updates for no apparent reason, and reopening the application will not resume the tracking process. We know that the app is not managing the location updates because the device logs show an event from locationd sending the position to our app, but there's no code execution.
We understand that the OS at any time can terminate the application, but we would like to be able to manage those cases to correctly communicate this behavior to the user that expects tracking to keep functioning or for the tracking to continue sending locations in the background.
This issue seems to be inconsistent across our user base, we've identified that users with iOS 14.4+ and from the iPhone 8 up to the iPhone 13 present this problem.
Our current configuration for the location manager is:
AppDelegate.m
#implementation AppDelegate
Location *geoData;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
///...
if (geoData == nil) {
geoData = [Location sharedInstance];
}
///...
}
- (void)applicationWillTerminate:(UIApplication *)application{
geoData = nil;
}
Location.h
#property (nonatomic, retain) CLLocationManager *locationManager;
Location.m
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDelegate:self];
if ([_locationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[_locationManager requestAlwaysAuthorization];
}
if ([_locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
_locationManager.allowsBackgroundLocationUpdates = YES;
}
_locationManager.pausesLocationUpdatesAutomatically = NO
_locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
We receive the updates through the didUpdateLocations where we send the data to an external API server.
The app is configured with Background fetch and Location updates background modes.

How can I get high accuracy location every 5 seconds or when user moves a threshold without draining the battery?

I have a location app that needs to get accurate location periodically. Currently I am getting constantly getting location in didUpdateLocation but I only ever log the location every 5 seconds. I am interested in a solution that gets accurate location periodically or on signification change. I would like either or both of these scenarios:
(by very accurate, I need 10m of desired accuracy)
Get a very accurate location every 5 seconds
Notify/callback if user moves a threshold ( eg moves 5 - 10 meters)
The app needs to work when backgrounded as well and location must still be logged if user switches to another app.
I was considering turning on/off location every 5 seconds but was not sure if that is the best practice. I also know there is also allowDeferredLocationUpdatesUntilTraveled but I believe that only applied to backgrounded mode. I would appreciate a solution that saves battery when the app is in use and in background mode. Please share your solutions and best practices for my use case.
I did write an app using Location services, app must send location every 10s. And it worked very well.
Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.
Steps are as follows:
Required: Register background mode for update Location.
Create LocationManger and startUpdatingLocation, with accuracy and filteredDistance as whatever you want:
-(void) initLocationManager
{
// Create the manager object
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
_locationManager.delegate = self;
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
_locationManager.desiredAccuracy = 45;
_locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[_locationManager startUpdatingLocation];
}
To keep app run forever using "allowDeferredLocationUpdatesUntilTraveled:timeout" method in background, you must restart updatingLocation with new parameter when app moves to background, like this:
- (void)applicationWillResignActive:(UIApplication *)application {
_isBackgroundMode = YES;
[_locationManager stopUpdatingLocation];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
}
App gets updatedLocations as normal with "locationManager:didUpdateLocations:" callback:
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
self.userLocation = newLocation;
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
But you should handle the data in then "locationManager:didFinishDeferredUpdatesWithError:" callback for your purpose
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
//do something
}
NOTE: I think we should reset parameters of LocationManager each time app switches between background/forgeround mode.
Hopefully this should help

Sending Latitude and Longitude to Server when app is in background

I have gone through so many links, even after that I haven't found a proper solution for getting latitude and longitude.
Periodic iOS background location updates
iOS long-running background timer with "location" background mode
I tried from some links and forums but it is working for only 3 mins, then app is not at all updating the user location.
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
//create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[locationManager startUpdatingLocation];
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(startTrackingBg) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
Ok.
After struggling for 3days, it is working for me for sending latitude and longitude when app is in background even after 3 mins.
I checked my app, continuously sending lat long for more than a hour in background.
It can help some one at least.
First Please add below two keys in your pList.
1.NSLocationAlwaysUsageDescription
2.NSLocationWhenInUseUsageDescription
Bothe are strings and you can give any value.
Then please turn on background fetch and check location updates under capabilities in project section.
Then import Corelocation framework and add this below code.
locationManager is a global variable.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//create CLLocationManager variable
locationManager = [[CLLocationManager alloc] init];
//set delegate
locationManager.delegate = self;
app = [UIApplication sharedApplication];
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
if ([locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]) {
[locationManager setAllowsBackgroundLocationUpdates:YES];
}
locationManager.desiredAccuracy = 45;
locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[locationManager startUpdatingLocation];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
[locationManager stopUpdatingLocation];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
locationManager.pausesLocationUpdatesAutomatically = NO;
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[locationManager startUpdatingLocation];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[locationManager stopUpdatingLocation];
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(startTrackingBg)
userInfo:nil
repeats:YES];
}
-(void)startTrackingBg {
[locationManager startUpdatingLocation];
NSLog(#"App is running in background");
}
//starts automatically with locationManager
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
latitude=newLocation.coordinate.latitude;
longitude=newLocation.coordinate.longitude;
NSLog(#"Location: %f, %f",newLocation.coordinate.longitude, newLocation.coordinate.latitude);
}
You need to refer this apple documentation handling location events in the background
You need to enable location updates in background modes in capabilities of your Xcode project.
The Standard location service wont work in background mode so you have to use either Significant-change location service or Visits service .
Use this code to
locationManager.delegate = self
locationManager.startMonitoringSignificantLocationChanges()
enable Significant-change location service.
Building on Santos answer which has most of the important steps to make background positioning work, I have clarified and corrected some details.
Project settings
You should add these keys to your Xcode Target Info property list. Make sure to add a valid description to each of them, or your app might fail approval.
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
NSLocationAlwaysAndWhenInUseUsageDescription
Next, in Target Capabilities, Turn on Background Modes and check Location updates and Background fetch. Location updates will enable locations in the background and background fetch will allow you to use the network in the background.
Start monitoring
Start by creating an instance of CLLocationManager, configure it and turn on background updates, then start it. Although the code below uses the most common function startUpdatingLocation to get locations, there are several other services to use. It is important to choose the most suitable service as this impacts greatly on battery usage and if the app will be re-launched or not by iOS. See below for more info on this.
// Declare as properties in your class
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) CLLocation *lastLocation;
// Call to start
- (void)initializeAndStartLocationService {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Must be set for background operation
self.locationManager.allowsBackgroundLocationUpdates = YES;
// Optional configuration
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.showsBackgroundLocationIndicator = YES;
// Authorize - the lazy way
[self.locationManager requestAlwaysAuthorization];
// Start the standard location service, there are others
[self.locationManager startUpdatingLocation];
}
// Delegate method that will receive location updates
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
// Keep the last received location
self.lastLocation = [locations lastObject];
NSLog(#"New position %f, %f", self.lastLocation.coordinate.latitude, self.lastLocation.coordinate.longitude);
// Do something with the location or retrieve the location later
// :
}
Stop monitoring
Don't forget to stop monitoring to conserve battery. You can start and stop several times on a single instance of CLLocationManager.
- (void)dealloc {
[self.locationManager stopUpdatingLocation];
}
Automatic app re-launch considerations
When your app is running in the background it can (and actually frequently will after some time) be terminated by iOS. Depending on the type of location updates you are using, iOS will or will not re-launch your app automatically for you. In cases where iOS do not re-launch, the user must start your app again to continue background processing.
Read more on this page.
Read more
CLLocationManager - Core Location | Apple Documentation
Handling Location Events in the Background | Apple Documentation

Background location tracking not working when the app is terminated

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/

Ranging Beacons only works when app running?

I am having difficulties getting this to work for when the app is not running. I have locationManager:didRangeBeacons:inRegion: implemented and it is called when the app is running in the foreground or background, however it doesn't seem to do anything when I quit the app and lock the screen. The location services icon goes away and I never know that I entered a beacon range. Should the LocalNotification still work?
I have Location updates and Uses Bluetooth LE accessories selected in Background Modes (XCode 5) I didn't think I needed them.
Any help greatly appreciated.
-(void)watchForEvents { // this is called from application:didFinishLaunchingWithOptions
id class = NSClassFromString(#"CLBeaconRegion");
if (!class) {
return;
}
CLBeaconRegion * rflBeacon = [[CLBeaconRegion alloc] initWithProximityUUID:kBeaconUUID identifier:kBeaconString];
rflBeacon.notifyOnEntry = YES;
rflBeacon.notifyOnExit = NO;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startRangingBeaconsInRegion:rflBeacon];
[self.locationManager startMonitoringForRegion:rflBeacon];
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
if (beacons.count == 0 || eventRanged) { // breakpoint set here for testing
return;
}
eventRanged = YES;
if (backgroundMode) { // this is set in the EnterBackground/Foreground delegate calls
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = [NSString stringWithFormat:#"Welcome to the %# event.",region.identifier];
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
// normal processing here...
}
Monitoring can launch an app that isn't running. Ranging cannot.
The key to having monitoring launch your app is to set this poorly documented flag on your CLBeaconRegion: region.notifyEntryStateOnDisplay = YES;
This can launch your app on a region transition even after completely rebooting your phone. But there are a couple of caveats:
Your app launches into the background only for a few seconds. (Try adding NSLog statements to applicationDidEnterBackground and other methods in your AppDelegate to see what is going on.)
iOS can take its own sweet time to decide you entered a CLBeaconRegion. I have seen it take up to four minutes.
As far as ranging goes, even though you can't have ranging wake up your app, you can make your app do both monitoring and ranging simultaneously. If monitoring wakes up your app and puts it into the background for a few seconds, ranging callbacks start up immediately. This gives you a chance to do any quick ranging actions while your app is still running.
EDIT: Further investigation proves that notifyEntryStateOnDisplay has no effect on background monitoring, so the above should work regardless of whether you have this flag. See this detailed explanation and discussion of delays you may experience
Code for iOS 9 to range beacons in the background, by using Location Updates:
Open Project Settings -> Capabilities -> Background Modes -> Toggle Location Updates and Uses Bluetooth LE accessories to ON.
Create a CLLocationManager, request Always monitoring authorization (don't forget to add the Application does not run in background to NO and NSLocationAlwaysUsageDescription in the app's info.plist) and set the following properties:
locationManager!.delegate = self
locationManager!.pausesLocationUpdatesAutomatically = false
locationManager!.allowsBackgroundLocationUpdates = true
Start ranging for beacons and monitoring region:
locationManager!.startMonitoringForRegion(yourBeaconRegion)
locationManager!.startRangingBeaconsInRegion(yourBeaconRegion)
locationManager!.startUpdatingLocation()
// Optionally for notifications
UIApplication.sharedApplication().registerUserNotificationSettings(
UIUserNotificationSettings(forTypes: .Alert, categories: nil))
Implement the CLLocationManagerDelegate and in your didEnterRegion send both startRangingBeaconsInRegion() and startUpdatingLocation() messages (optionally send the notification as well) and set the stopRangingBeaconsInRegion() and stopUpdatingLocation() in didExitRegion
Be aware that this solution works but it is not recommended by Apple due to battery consumption and customer privacy!
More here: https://community.estimote.com/hc/en-us/articles/203914068-Is-it-possible-to-use-beacon-ranging-in-the-background-
Here is the process you need to follow to range in background:
For any CLBeaconRegion always keep monitoring on, in background or foreground and keep notifyEntryStateOnDisplay = YES
notifyEntryStateOnDisplay calls locationManager:didDetermineState:forRegion: in background, so implement this delegate call...
...like this:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
if (state == CLRegionStateInside) {
//Start Ranging
[manager startRangingBeaconsInRegion:region];
}
else{
//Stop Ranging
[manager stopRangingBeaconsInRegion:region];
}
}
I hope this helps.
You are doing two separate operations here - 'ranging' beacons and monitoring for a region. You can monitor for a region in the background, but not range beacons.
Therefore, your implementation of locationManager:didRangeBeacons:inRegion: won't get called in the background. Instead, your call to startMonitoringForRegion will result in one / some of the following methods being called:
– locationManager:didEnterRegion:
– locationManager:didExitRegion:
– locationManager:didDetermineState:forRegion:
These will get called in the background. You can at that point trigger a local notification, as in your original code.
Your app should currently wake up if you're just wanting to be notified when you enter a beacon region. The only background restriction I know of concerns actually hosting an iBeacon on an iOS device. In that case, the app would need to be physically open in the foreground. For that situation, you'd be better off just doing the straight CoreBluetooth CBPeripheralManager implementation. That way you'd have some advertising abilities in the background.

Resources