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 th
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 This can launch your app on a region transition even after completely rebooting your phone. But there are a couple of caveats:CLBeaconRegion
: region.notifyEntryStateOnDisplay = YES;
NSLog
statements to applicationDidEnterBackground
and other methods in your AppDelegate to see what is going on.)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
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.
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:
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.