Caveats for background iBeacon Region Ranging - ios

Apple explicitly discourages background iBeacon Ranging:
To promote consistent results in your app, use beacon ranging only while your app is in the foreground.
If your app is in the foreground, it is likely that the device is in the user’s hand and that the device’s view to the target beacon has fewer obstructions.
Running in the foreground also promotes better battery life by processing incoming beacon signals only while the user is actively using the device.
Should I choose to go naughty and do some ranging in the background (after entering a beacon range when monitoring), what consequences should I expect? (except for the famous 10-seconds running window before being put to sleep again?

In most cases, there is no reason to worry about battery drain from casual background ranging.
I've read that statement before, but I don't think it makes any sense, and suspect it was written before the CoreLocation iBeacon design was complete. (The statement has been there since the iOS 7 beta.)
Apple iOS generally enforces that you can only range in the background for 10 seconds at a time, typically triggered by a CLBeaconRegion monitoring entry/exit event. Unless you app is in an extreme situation where it is seeing beacon regions enter and exit all the time, 10 seconds of background bluetooth scanning just can't use that much battery.
Of course, there are techniques where you could range more often, such as requesting location background mode for constant ranging, or requesting an extra 3 minutes of ranging time as described in my blog post here. If you use one of these techniques, you should probably test the impact of your app on battery. But I certainly wouldn't describe doing these things as being "naughty" as long as you do so responsibly.

Related

Minimise battery consumption on Beacon Ranging in iOS while using iBeacon

I'm working on a use case that needs continuous callbacks while scanning the beacons.
I've thought of 2 approaches but they both have issues.
Monitoring: Monitoring only gives entry and exit callbacks. There's a limitation of listening to only 20 beacons. Also is there a range that i can define to get entry and exit callbacks? Like say if a an advertising beacon comes in range of 2 meters i get an entry callback and if the device moves out of that range i get an exit callback.
Ranging: Ranging gives continuous callbacks along with a set of other parameters like rssi to calculate distance. The big issue when it comes to ranging though is that it consumes insane amount of battery compared to monitoring. What should be an approach for getting continuous callbacks while optimising battery consumption?
I've tried both the approaches and reached to a dead-end. Hence it may seem like a theoretical question yet any insights to solve the use-case in some manner.
It is not possible to set any kind of rssi or distance filter when using iOS CoreLocation beacon monitoring APIs. Ranging is the only alternative when working with iBeacon.
While battery drain of constant BLE scanning is an issue, you an mitigate this by scanning on a lower duty cycle as needed to meet your requirements. For example, you can scan on a 20% duty cycle (and use 20% as much battery as constant ranging) by ranging for 12 seconds every minute. You can adjust this duty cycle as needed to balance your goals between battery savings and responsiveness. I have worked on projects where I change this duty cycle depending on app state, so the app can be more responsive to beacons when it is important and save battery when responsiveness is less important.
In order to be able to do this at all, you must unlock the ability to have iOS let your app run in the background for an unlimited time period as described in my blog post here:
Add the location background mode to Info.plist
Obtain "always" location permission from the user. It is not sufficient to get "when in use" permission.
Start a background task as described here: http://www.davidgyoungtech.com/2014/11/13/extending-background-ranging-on-ios
Request 3km location updates from CoreLocation. This will keep the app running in the background without extra battery drain from GPS, as 3km accuracy only uses the cell radio You don't need to do anything with these results. You just need to request them to keep the app alive.
Once you do the above, you can call locationManager.startRangingBeacons(...) and locationManager.stopRangingBeacons(...) on a timer to implement whatever duty cycle you want.

How can I get major and minor numbers in one UUID on iOS background monitoring status?

We are using one UUID, and major and minor combinations for different actions.
And we need to know major and minor numbers in iOS background monitoring.
The ranging can get major and minor numbers but this needs launching delay and battery consuming. So this is not the proper solution for us because we want immediate detecting and low battery consuming.
So we want to get major and minor numbers in same UUID on iOS background monitoring status.
This mechanism is necessary because we make an iOS application not for typical usage.
Is it possible?
You cannot read individual beacon identifiers using monitoring APIs. All you can do is access the CLBeaconRegion identifiers used to start monitoring. In your case, this is probably just the ProximityUUID with a nil major and minor.
The alternative is to combine ranging with background monitoring. Whenever you get a didEnterRegion event, you will also get ranging callbacks for about 10 seconds afterward, even if your app is in the background. You can use this callback to read all the identifiers.
While it is true that ranging in the foreground uses much more battery than monitoring, background ranging is actually pretty battery friendly. Consider that you will be only ranging for 10 seconds each time you enter or exit a region. (Even though ranging is still turned on, the OS automatically stops it after 10 secs in the background.) Unless you expect user to be constantly entering/exiting regions, then battery should not be a concern with such short periods of background ranging.

Detecting beacons via iBeacon Monitoring & Ranging vs CoreBluetooth scanForPeripheralsWithServices

There is a lot of confusion regarding the restrictions that are applied by the iOS on apps that want to scan BLE beacons\peripherals.
After reading several blogs and Stack Overflow answers, I want to see if I understand all the issues correctly. Please correct me if there is anything I misunderstood or missed. I refer only to iOS 7 and above, and focus on detection and not connection (Can you connect to a CLBeacon using the iBeacon Monitoring & Ranging API?).
The options for the beacons are clear - Use a general purpose BLE peripheral or use a BLE peripheral that advertises in the iBeacon format (Also, a non-standard peripheral can advertise in the iBeacon format in the adv-packet and a different format in the scan-response packet).
General Restrictions
iBeacon Ranging will let you know which beacons are around you. You must specify the ProximityUUID that the beacons advertise beforehand (no "general" scanning). didRangeBeacons will be called every second with an array of CLBeacon objects that were found recently. The distance from the beacon and its accuracy are calculated by the iOS using some confidential algorithm that only Apple's developers really know (The algorithm is based on the rssi values and the rssi-at-1-meter calibration byte that the beacon advertises). You can also use iBeacon Monitoring to call a delegate every time you enter or exit a region - again you must specify the ProximityUUID that you are looking for (you can also specify a major & minor). "Exiting a region" is defined by some time of not receiving any advertisement, and therefore cannot be immediate. The number of regions that can be ranged\monitored simultaneously per device is limited to 20 - This means that if other apps do monitoring\ranging at the same time, your app may not be able to monitor\range (right?).
CoreBluetooth - You can also detect other ad-structures in the beacon's advertisement. If the beacon advertises in iBeacon format too, you cannot see the iBeacon fields (ProximityUUID, major, minor...), despite the fact that they are sent under a standard "Manufacturer Specific" ad-structure that you can see in other cases.
Running in the Foreground - The less restricted use-case:
iBeacon Ranging and Monitoring - no further restrictions.
CoreBluetooth - Passing nil in the serviceUUIDs of scanForPeripheralsWithServices will scan for all peripherals. Passing CBCentralManagerScanOptionAllowDuplicatesKey as YES in the options will make the didDiscoverPeripheral to be called multiple times for the same peripheral\beacon (I assume that using a timer you detect the advertisement was not received for some time and assume that the user exited the "region").
Running in the Background - The more restricted use-case:
iBeacon Ranging will not work directly. iBeacon Monitoring will call didEnterRegion and give the app runtime of 6 seconds - in which you can start Ranging (for example, to detect major & minor). The detection may not be immediate since iOS turns scanning on and off to preserve the battery power. If you enter a region of multiple beacons with the same ProximityUUID, and you monitor this UUID without a specific major and\or minor, didEnterRegion will be called when you start receiving the signal from the first beacon - however, if you did not exit the region of the first beacon and you also entered the region of a second beacon the app will not be woken up again (didEnterRegion will not be called again) so you cannot start ranging to detect the second beacon's major & minor. The app cannot simply pop up to the foreground, but can create local notifications and other background operations.
CoreBluetooth - According to Core Bluetooth Background Processing scanForPeripheralsWithServices can run in the background using, but you must specify at least one serviceUUID. didDiscoverPeripheral will be given a runtime of 10 seconds. Using CBCentralManagerScanOptionAllowDuplicatesKey will not work - didDiscoverPeripheral will be called once for every peripheral. Therefore, you cannot detect "exit" from the region and "re-entry". I suppose you can use a non-standard BLE peripheral that changes its MAC address to overcome this issue. The app cannot simply pop up to the foreground, but can create local notifications and other background operations. The detection may not be immediate since iOS turns scanning on and off to preserve the battery power.
Running after the app is killed
iBeacon Monitoring - Works! Even if the user killed the app or the device was restarted.
CoreBluetooth - The app will be woken up if it was killed by the iOS (due to inactivity or memory constraints). However, if the user explicitly killed the app it won't be woken up (which makes the first case hard to test). I don't know what happens after a device restart...
Does anyone have more experience with these restrictions? Can scanForPeripheralsWithServices be used as a better alternative to iBeacon Monitoring in some use-cases?
Thanks!
You are mostly right with your description. Just two clarifications:
The 20 region limit is not per device, it is app-specific. No matter what other apps are doing on the mobile device, your app is still allowed to monitor up to 20 regions by iOS. That said, there are likely hardware limits that are device-specific on how many regions can be monitored in the background with hardware assistance. These limits are undocumented. If you surpass these undocumented limits, it will probably take a lot longer to detected beacons in the background. (Although that said, there is no OS guarantee of when the detections come, anyway.)
You cannot connect to a CLBeacon using Monitoring and Ranging APIs. These APIs only work with BLE advertising packets, which are connectionless.
Yes, it is possible to use scanForPeripheralsWithServices as an alternative. This is what Gimbal beacons do in order to implement a proprietary system. There are real disadvantages, however, in terms of background detection time and reliability.

Time spend around a beacon

What should be the correct approach to identify time spend around a beacon[not within a region] in background. I am able to do this when app is in foreground using didRangeBeacons and some business logic.I read on few forums that ranging does work when app has registered for location updates in background, but i am having no success. I have added the location updates key for UIBackgroundModes in plist.
I am using estimote beacons and their sdk.
I see two possible solutions here:
Listen for enter and exit region events, store the timestamps and then use them to calculate the time span on exit. If you define your region so that it encompasses only one beacon, monitoring the region will be equivalent to monitoring the beacon. The only thing to keep in mind is that iOS imposes a limit of 20 regions to be monitored at the same time - so this solution doesn't scale above 20 beacons.
Use ranging in the background. Apart from the UIBackgroundModes, you also need to start regular location services, i.e. startUpdatingLocation.
Beacon ranging delivers events normally while an app is running in the foreground. When your app is in the background, this service delivers events only when the location-updates background mode is enabled for the app and the standard location service is running.
(this is from CLLocationManager class reference, section "Using Location Services in the Background")
Note that ranging in the background will be draining the battery life more than usual, and Apple also requires justification for using the background modes. Unless there's some value for the user of your app coming from the background modes, they might choose to reject it. All in all, use the background ranging wisely! (:

How long does it take to detect an iBeacon while region monitoring?

Take this scenario: user has an iPhone in the pocket passing by an iBeacon. Her phone is region monitoring for it. How much time is needed in real world to from entering the region to the moment app is woken up?
I have found an excellent article on the subject by David G. Young (http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html) but I can't believe it can really take up to 15 minutes.
That would mean that all iBeacon home automation scenarios are simply unusable because you won't neither wait 15 mitutes in dark room for lights to switch on neither you would switch on the lights by actively using an app. Door opening and locking is another situation where iBeacon would be unusable (and NFC would work much better here). Or am I missing something?
I have an app that ranges for an iBeacons. It first grabs a list of couple proximityUUIDs and registers them as monitored regions.
When the device enters that region, it takes just 1 to several seconds (iPhone 5S) to post me a local notification on didEnterRegion: event.
When the app is in front, it starts ranging the beacon immediately in current region (if any) and updates happen in about one-second intervals.
When the app is in background, ranging is not enabled, otherwise it would immediately report that the beacon is gone (if you leave its range).
HOWEVER, It can really take up to 15 minutes (I've experienced this) for the device to post the didExitRegion: when in background in the worst case, when there is just significant location monitoring enabled combined with bad or no network. Otherwise it happens until about a minute.
Sorry, this didn't fit into a comment.

Resources