0

My goal is to find all iBeacons nearby an I plan to send local notification when new ibeacon is found. I am assuming all of them will have the same proximity UUID. And I will use combo of major and minor ids to identify them (each user will have unique combination of major & minor id).

I am assuming I need to monitor for this specific UUID and that locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) will be triggered only for the first UUID right? So when it happens I need to startRanging to find every beacon nearby except the one Ive found. What will be achieved by callback from locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion). But the question is what will happen if new beacons will appear nearby? I need to fire startRangin also on didExitRegion? I am only afraid that if the one already found beacon will stay in range for like 1 hour, I won't be able to detect any other beacons which will appear in this region/range.

Any advice for most efficent search?

szibi
  • 141
  • 7

2 Answers2

1

You will initially be monitoring for a CLBeaconRegion that specifies only the beacon uuid you are interested in.

When any beacon advertising this uuid is detected you will get a call to didEnterRegion.

At this point you should call startRangingBeacons(satisfying constraint:) where your CLBeaconIdentityConstraint specifies only the uuid.

You will then get calls to the locationManager(_:didRange:satisfying:) delegate method.

You can examine the minor and major values of the CLBeacon objects passed to the delegate function to determine the beacons that are in range and their range. As beacons satisfying the constraint come into range they will be delivered to the delegate, even if they weren't originally in range when the region was entered.

At some point there will be no beacons with the specified uuid and you will get a call to didExitRegion. At this point you should stop beacon ranging.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Hmm, the thing is I want to use this mechanism for scanning in the background. If I will startRanging from didEnterRegion callback it will notify me for X time about all nearby beacons? I am wondering how much time it scan be ranging in the background triggered by didEnter/didExitRegion callbacks. Because at the moment when I am using ranging right after monitoring: It notifies me when first beacon is found in `locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion)` but it doesnt notify me when another beacon the same range. – szibi Feb 09 '23 at 10:51
  • Background adds additional complexity. Ranging works while your app is running in the background, but ranging on its own is not sufficient to *keep your app running in the background*. See [this](https://stackoverflow.com/questions/29984122/ibeacon-monitoring-and-ranging-in-background) and [this](https://community.estimote.com/hc/en-us/articles/203914068-Is-it-possible-to-use-beacon-ranging-in-the-background-) for some ideas – Paulw11 Feb 09 '23 at 11:51
  • Also, note the approach in my answer. You should range for a beacon constraint and implement the didRange for constraint delegate method. – Paulw11 Feb 09 '23 at 11:53
1

Using a backgrounded iOS app to track multiple BLE beacons with the same UUID and differing major and minor values is challenging. (Ranging normally times out after 10 seconds in the background.) Two basic approaches:

Solution #1: Constant Ranging While in Region

For this approach, you need to set up your app to unlock unlimited ranging in the background while you are inside the beacon region. This is a bit tricky, and will use significant battery if you are around beacons for long periods of time. But is legal to do for App Store distribution provided that your app makes it clear that it uses location in the background for an approved purpose.

I documented this in a blog post here: http://www.davidgyoungtech.com/2023/02/10/forever-ranging

The basic steps are:

Setup:

  1. Put the following declaration in your Info.plist
    <key>UIBackgroundModes</key>
    <array>
        <string>location</string>
    </array>
  1. Obtain NSLocationAlways permission from the user.
  2. Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
  3. Start ranging for the same region. There is no real reason to turn ranging off as logic below will handle throttling whether the OS allows it to do work in the background.
  4. In your didRange callback, add logic to process all the detected beacons.

On Region Entry:

  1. Start location updates at the lowest power setting (basically just using cell radio) with:
        locationManager.pausesLocationUpdatesAutomatically = false
        locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        locationManager.distanceFilter = 3000.0
        
        if #available(iOS 9.0, *) {
          locationManager.allowsBackgroundLocationUpdates = true
        } else {
          // not needed on earlier versions
        }
        // start updating location at beginning just to give us unlimited background running time
        self.locationManager.startUpdatingLocation()
  1. Start a background task as described in my blog post here: https://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios

On Region Exit:

  1. Stop the background task from the previous step
  2. Stop the location updates with self.locationManager.stopUpdatingLocation(

The two changes above will effectively make it so you no longer get ranging updates in the background when no beacons are visible, which will save battery.

Solution #2: Use One Extra Region Per Beacon

This solution is not foolproof, but has the advantage of not requiring a constant background task and background updates. Depending on the placement of your beacons and how users move between them, you will not guarantee detecting each one. But it works for many cases where beacons are not typically placed close together.

  1. Obtain NSLocationAlways permission from the user.
  2. Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
  3. Start ranging for the same region. There is no real reason to turn ranging off as logic below will handle throttling whether the OS allows it to do work in the background.
  4. In your didRange callback, add logic to process all the detected beacons.

Each time you detect a new beacon (with a different major and minor) do the following:

  1. Start monitoring a new CLBeaconRegion with the UUID, major and minor of the known beacon. This will allow you to get a didExit callback when it disappears. Because iOS only allows you to monitor 20 regions, you can only do this for 19 additional regions at the same time.

On Region Exit for a CLBeaconRegion for a specific major and minor:

  1. Stop monitoring that region.

The advantage of this approach is that if you have a large number of beacons in overlapping transmitter range, you will get an additional didExit callback as you go out of range of each one. Each time there is a region transition (entry or exit) you get another 10 seconds of background ranging time. This allows you an opportunity to look for new beacons in the area periodically.

This second solution is not perfect. If you encounter beacon A, then it stays in range for 20 seconds before you encounter beacon B, you won't get a ranging callback for beacon B because ranging times out after 10 seconds. In this scenario, you might detect beacon B later on if beacon A goes out of range before beacon B does (the region exit from beacon A gives you more ranging time), but if beacon A does not go out of range before beacon B, then you will never detect beacon B.

Which Solution Should You Use?

Use solution #1 if you must have perfect detections, you app obviously provides a location-specific benefit to the user, and you are OK with the battery drain of constant ranging while beacons are around.

Use solution #2 if you don't need perfect detections, if beacon placement is tolerant of the technique's shortcomings, or if you can't live with the battery drain or constant background ranging for other reasons.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Thanks a lot for this answer, I think at the beginning I'll go with first option! – szibi Feb 14 '23 at 08:11
  • Yet I am trying to figure out how to handle ios devices which are both in the background (advertiser as well). As the topic is slightly different I have created another question. https://stackoverflow.com/questions/75445205/how-to-scan-ios-apps-in-background-or-exchange-data-between-two-apps-in-backgrou – szibi Feb 14 '23 at 08:41