Beacon Ranging in Background on iOS

IBeacon background monitoring

Apple restricts the duration in which iOS apps may "range" beacons in the background. This is true as of April 2020, and the state of affairs is largely unchanged in the last 5-6 years.

A few clarifications:

  1. Beacon monitoring is not restricted in the background, provided the user grants "location always" permission to the app. But beacon monitoring only gives you "entry" and "exit" events. It gives you an entry event when any beacon you are looking for first appears, and an exit event when the last of the beacons you are looking for disappears. Monitoring won't tell you anything about the estimated distance or signal strength of an individual beacon.

  2. Beacon ranging lets you get updates every second with a list of all beacons visible, each beacon's signal strength and a distance estimate. You can easily program logic to write to a database if the distance estimate is under as certain threshold.

  3. Beacon ranging is restricted in the background on iOS. First, the user must grant "location always" access to your app. And even then you are limited to about 5-10 seconds of ranging after your app goes to a background, or beacons first appear after it is in the background.

  4. You can extend the 5-10 seconds mentioned in (3) to 180 seconds simply by running a background task per my blog post here.

  5. You can further extend the 180 seconds indefinitely if you declare "location" background mode in your Info.plist, use a background task as described in (4), and you use CoreLocation to request location updates (coarse location updates with only a 3km resolution work fine for this purpose and save battery by keeping the GPS off.)

  6. One problem with (5) is that it may get your app rejected if you plan to submit it to the App Store and you don't provide an obvious user-facing benefit to using location in the background. If you do have a good justification, or you intend to distribute the app outside the App Store (e.g. a corporate enterprise app), then this will work fine.

  7. A second problem with (5) is that ranging all the time can cause significant battery drain. You may need to program extra logic to stop this constant background ranging at times where it is not needed in order to save battery.

This is admittedly confusing and complex. But the bottom line is that there are ways to range beacons indefinitely to satisfy many use cases. You just have to jump through some hoops to make it happen.

iBeacon monitoring and ranging in background

The problem you describe is common. Other than extending background ranging time (which you have already done), there is no magic bullet on iOS.

Two additional techniques may help. Both of these techniques involve getting a new entry event as you pass from one beacon to the next:

  1. Get hardware beacons that allow you to tune down the transmitter power (my company's RadBeacon products allow this) so the transmissions do not overlap. This way you get an exit event then a new enter event as you move from beacon to beacon.

  2. Redesign your identifier schene so the major field is dedicated to identifying up to 20 different regions (based on UUID and major 1-20). You then monitor for all of these regions. Your individual beacons can still use the minor however you want and specifically as the key to trigger messaging. When placing your beacons, you make sure that none with overlapping transmissions share the same major. This will ensure a new background entry event as you move from one to the other.

Ranging beacons in background

On iOS, apps are limited to ranging for 5 seconds in the background. This timer is restarted each time the app is put to the background, or when a beacon monitoring event (entered region / exited region) fires. The good news is that you can extend the time allowed to range beacons in the background to 3 minutes after each of these events.

I put together a blog post that shows you how to do it here.

iOS Extending background time for beacon monitoring

What you want to do is possible.. But it is tricky.

A few points:

  1. In the background, you cannot ever detect AltBeacon or other manufacturer advertisements With CoreBluetooth as iOS blocks manufacturer advertisements from being delivered in the background. (iBeacon advertisements follow different rules in the background when you use CoreLocation.)

  2. In the background, service advertisements like Eddystone can be detected.

  3. Generally your app can only run for a few seconds after being sent to the background, then detections will stop. However, you can extend this time by up to 180 seconds upon request using the technique described here. While my blog post is about CoreLocation, the same technique works for CoreBluetooth.

  4. As the above article mentions, it is also possible to extend the 180 seconds to forever by doing the following:

  • Set the BackgroundModes in your Info.plist to include location.
  • Obtain location always permission in addition to Bluetooth permission.
  • Use CoreLocation to start major location updates. (You do not have to use the location results that come from this, but it is needed to keep the app alive in the background.)
  • Start a background task as described in the blog post linked above.

This will keep your app scanning for Bluetooth service beacons like Eddystone in the background forever, but there are three catches:

  1. If you want to get your app approved for the App Store you must convince Apple reviewers that your app provides the user a location-specific benefit that is obvious to the user.
  2. iOS will present your users a scary dialog every 3 days or so warning them the app is using your location in the background and showing a map with all the places the user was when the app was running. The dialog will allow the user to disable background location permission, and if the user does this, background beacon updates will stop. See my blog post here for more info.
  3. There will be significant battery drain while your app is doing constant Bluetooth scanning.

Beacon range in background

When an app is in the background and it gets a didRangeBeacons callback, it only gets 5 seconds to run by the operating system before it is suspended. This will close any web service connections that are open at that time. You can extend this background running time from 5 seconds to 180 seconds upon request. Below is an example in Swift 3 that shows how to do that.

var threadStarted = false
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid

func extendBackgroundRunningTime() {
if (self.backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return
}

print("Attempting to extend background running time")

self.backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "DummyTask", expirationHandler: {
UIApplication.shared.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
})

if threadStarted {
print("Background task thread already started.")
}
else {
threadStarted = true
DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.default).async {
while (true) {
// A dummy tasks must be running otherwise iOS suspends immediately
Thread.sleep(forTimeInterval: 1);
}
}
}
}

By adding code like this, it is much more likely that your web service call will complete before iOS suspends your app.

You can call the extendBackgroundRunningTime() from your didRangeBeacons or didEnterRegion methods.

Need help in integrating iBeacon + BLE Peripheral Scanning iOS Background mode feature

iOS will launch your app upon beacon detection and you will get a didEnter(region:) callback. At this time, you can immediately start beacon ranging with locationManager.startRangingBeacons(...) and you will get didRangeBeacons(...)callbacks for a few seconds -- long enough to read the major/minor identifiers and do the work you need.



Related Topics



Leave a reply



Submit