At Radius Networks, we’ve had our SDKs added to a ton of beacon apps, and we’ve tested a large percentage of them.
One of the most frustrating things about working with beacon-enabled apps is testing background triggering. Usually, iPhone 5 and later trigger in the background within a second of seeing a beacon. But sometimes it takes longer. Much longer.
In many cases, we have observed iOS devices behaving as if some sort of beacon region monitoring resource limit had been exceeded. So we had a question…
Apple’s documentation states that each app has a limit of 20 beacon regions, but the documentation doesn’t say anything about a global limit for a device. We decided to investigate if such a limit exists and how exceeding that limit may affect beacon region monitoring behavior.
The results of our investigation are that a global beacon region monitoring limit does exist. The regions are registered first-in-last-out, so that the first regions registered are in the “priority” group, and the latter ones will get bumped to the “best effort” group.
The priority group can contain up to 30 beacon regions. Applications will receive almost immediate region monitoring call backs (ie didEnter Region & didExit Region) for priority group beacon regions regardless of the state of the iOS device, including the black screen state, lock screen state, springboard state and all application states.
The best effort group contains beacon regions registered beyond the 30 beacon region limit. Applications may experience significantly delayed region monitoring call backs based on the state of the mobile device. For example, if a mobile device is in the black screen state, meaning the screen is not illuminated at all, applications may never receive region monitoring call backs for best effort group beacon regions until the user or some event like a push notification, calendar reminder or text message causes the device to wakeup.
Limiting the priority group to the first 30 registered beacon regions seems fairly small given that each app can register up to 20 regions, so we believe this is a very significant finding.
Apple’s iOS CoreLocation framework is closed source, so this is just speculation, but our best guess is that there is hardware support for monitoring a limited number of beacon regions. After those slots are taken, iOS falls back to software monitoring and makes a best effort to monitor for beacon regions. However in an effort to save battery life iOS may suspend beacon region monitoring by device power management.
What does this mean to you as a developer of beacon-enabled apps on iOS? If your app is not one of the first ones to be installed on an iOS device, your app may be ineligible for fast background detection and relegated to the slow lane. It is therefore important to design your app to take this possibility into account. You must set expectations appropriately when telling clients and app testers how quickly background detections will take place. Since testers are especially likely to have multiple beacon-enabled apps on their devices, and therefore most likely to experience the delays described here. To speed up detection during testing, uninstall other beacon-enabled apps and reboot your phone before installing your app for testing.
Since there are so many different permutations and testing can take so long, I decided to come up with a baseline configuration to test under. Obviously, this is not necessarily the conditions always seen in the wild, but was the best way to characterize the behavior I’d been noticing.
The experiments were run with the following devices:
I executed the following procedure to establish a baseline configuration:
On each subsequent run I followed this procedure:
After test is run, update and redeploy any apps and repeat the steps above.
I created an Xcode project with 10 targets that will deploy the same code as 10 different apps. This lets you make minor changes to region settings and redeploy all the apps to the device.
The main approach I took was to install all 10 apps to the device, starting at 1 and going in order. Make sure that each starts up and can see it register the regions in the logs. This would be done with different numbers of regions. For example I might do a run where each app has 5 regions, deploy them all to the phone and run the experiment.
The most predictable way I found to see where this limit is:
An example run might look like:
10 apps @ 7 regions
|App||UUID Region||Time till notification|
|10||7||None (5 min wait)|
|10||6||None (5 min wait)|
|10||5||None (5 min wait)|
|10||4||None (5 min wait)|
|10||3||None (5 min wait)|
|10||2||None (5 min wait)|
|10||1||None (5 min wait)|
|9||7||None (5 min wait)|
|9||6||None (5 min wait)|
|9||5||None (5 min wait)|
|9||4||None (5 min wait)|
|9||3||None (5 min wait)|
|9||2||None (5 min wait)|
|9||1||None (5 min wait)|
|8||7||None (5 min wait)|
|8||6||None (5 min wait)|
|8||5||None (5 min wait)|
|8||4||None (5 min wait)|
|8||3||None (5 min wait)|
|8||2||None (5 min wait)|
|8||1||None (5 min wait)|
|7||7||None (5 min wait)|
|7||6||None (5 min wait)|
|7||5||None (5 min wait)|
|7||4||None (5 min wait)|
|7||3||None (5 min wait)|
|7||2||None (5 min wait)|
|7||1||None (5 min wait)|
|6||7||None (5 min wait)|
|6||6||None (5 min wait)|
|6||5||None (5 min wait)|
|6||4||None (5 min wait)|
|6||2||None (5 min wait)|
|6||1||None (5 min wait)|
|5||7||None (5 min wait)|
|5||6||None (5 min wait)|
|5||5||None (5 min wait)|
|5||4||None (5 min wait)|
|5||3||None (5 min wait)|
I ran this test under many configurations with similar results. I always found the limit to be 29-30 regions. I assume something is going on that registers something in the hardware layer. I am not sure how best to control the configuration, but believe the inconsistency to be a lack of clean baseline.
Other configuration I tried:
I also ran a number of ad-hoc experiments to see what would happen under other circumstances, but those were often run on just one device and not repeated.
Rebooting the device and waiting. The regions were still registered in the same order.
Uninstalling an app does not re-register any best effort regions. So if I removed the app #1, it didn’t free up the “priority” region slots. Most likely this will be freed up as the apps are re-registering or the device is rebooted. But I didn’t try that.
I spent a fair amount of time trying to characterize region monitoring behavior for beacons in the best effort group. I would often turn on a beacon and wait for up to 30 minutes to see if it would wake up the app and send a notification. Sometimes it would, most of the time it wouldn’t. But then again, I didn’t wait for 30 minutes that often.
This research does not characterize the amount of time it takes to detect in the background when the fast background detection slots are exhausted. However, previous research shows that iPhone 4S devices (which apparently do not have fast background detection at all) may take as long as 15 minutes to trigger in the background.
This was a very interesting couple of days worth of work. I certainly learned quite a bit about how iOS registers beacon regions. I would be curious what your experience is, in particular if you wanted to run controlled tests using the code above.
I also want to note that this is likely to change as Apple makes updates to iOS and their hardware. We already know that there are innate differences in the older iPhone 4 models, and I am sure they will continue to tweak things going forward.
If you run similar experiments let us know, we’d love to compare notes.