Detection of all iBeacons nearby with the same uuid on ios - ios

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?

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:
Put the following declaration in your Info.plist
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Obtain NSLocationAlways permission from the user.
Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
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.
In your didRange callback, add logic to process all the detected beacons.
On Region Entry:
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()
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:
Stop the background task from the previous step
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.
Obtain NSLocationAlways permission from the user.
Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
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.
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:
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:
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.

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.

Related

When do I get notified when monitoring an iBeacon region based on UUID?

I want to build that gets notified (even when in the background) when it gets in range of any iBeacon with a given UUID.
I know I could monitor an individual region for each of my beacons, but if I understand correctly, on iOS, I am limited to 20 monitored regions.
So the other alternative is to monitor a region based solely on its UUID, but then when do I get enter/exit notifications for that region? Whenever I get in range or out of range of any beacon with the same UUID, or only when I get in range of the first one and out of range of the last one?
If you monitor based on a wildcard region that includes only a ProximityUUID (leaving major and minor nil) then :
You will only get one didEnter callback the first time the first beacon is detected. If a second, third, or additional beacon is detected matching this wildcard region, then you did not get any monitoring callbacks.
You won't get any further callbacks until all beacons matching the wildcard region disappear (this takes at least 30 seconds in the foreground, longer in the background.) At that time, you'll get a call to didExit. After receiving a didExit, you will again be eligible for the callback in (1) above.
If you want more granular callbacks about individual beacons there are a few choices:
Use additional region definitions (up to the 20 allowed) and monitor them all.
Dynamically change the regions you are monitoring when you get a didEnter. This might work if you have a limited number of beacons (e.g. less than 20) matching a particular wildcard region. You could then activate monitoring for each of these sub-regions.
Use ranging APIs. These give you callbacks once per second with a list of all matching beacons seen that match a region. The trick is that this is generally limited to 10 seconds after region transition in the background. But it is possible to extend it to 180 seconds on request, or indefinitely if you declare your app to be a background location app in Info.plist. (If you go this route, there are a few tricks to get this working properly.)

Are iBeacon major/minor values available when monitoring a region?

I am trying to build a test app that would detect a given set of iBeacon deployed in a few physical locations. The detection should be done in the background, and make a call to a server following a certain logic.
Apple's rule of thumb is using the same UUID for all of my physical locations:
The UUID is shared by all locations.
This allows an iOS device to use a single identifier to recognize any of the stores with a single region.
Each specific store, San Francisco, Paris, and London, is then assigned a unique major value, allowing a device to identify which specific store
it is in.
I found the call for setting a region with a given UUID, but did not find what object is returned when the UUID is detected, and what fields are returned.
Where does the app get the major/minor value when iOS detects a beacon with a given UUID?
When using monitoring APIs, you only get a CLBeaconRegion object in the enter/exit callback from CoreLocation. A beacon region defined by this class typically doesn't represent individual beacons, but a range of beacons based on wildcard identifiers.
Because of this, if you use wildcards in your region definition (as is typical) you actually can't see which specific beacon triggered a region entry. To solve this problem, you must combine both monitoring and ranging APIs at the same time. If you monitor and range simultaneously, CoreLocation will give you a callback in the didRangeBeacons beacons: [CLBeacon] inRegion region: CLBeaconRegion method with an array of all the CLBeacon objects it saw.
Because the callback with the array of beacons arrives once per second, however, you will need to add custom logic to do something only once when beacon first appears, if necessary.
Since you want background detection, it is important that you do both ranging and monitoring simultaneously. Even if you don't put anything in your monitoring callback because of the lack of specific beacon information, you need to do monitoring just to get your app woken up in the background on a new beacon detection. Then, you will get ranging callbacks for approximately 10 seconds before your app is suspended again.

iBeacon case - trigger didEnterRegion with distance constraints

For a specific business case, I would like to trigger proximity-based notification (push or UILocalNotification) by leveraging the iBeacon technology.
This is aimed to work while the app is in background/lockscreen.
I have some specific constraints :
The product has to scale at a certain level so it's not possible to range 20 regions. We will range on one UUID only (maybe 2 or 3 if we develop new set of features, but we will not register a region per physical beacon)
We will use the major and minor to call webservices
Regarding this, I know I will have to use the following approach : first didEnterRegion: with no prior information on the major/minor/distance, and then didRangeBeacon: to perform more advanced actions.
I already use local storage mechanisms to timeout a beacon after he has been used considering the fast beaming rate of beacons.
* The major constraints : we have to region events regarding to the distance of the beacon (exemple, only trigger the notification if CLProximityImmediate)
Now I see a major limitation. As my app will only range in background for a few seconds after it entered in a region, if I had constraints based on the distance/proximity, it is very possible that the app will:
return to background state before the user get close enough from the physical beacon for the event to be triggered
never be triggered again because it has to enter the region to range again, which will probably not happen
Do you have any ideas / work around for such a case ?
You are absolutely correct on the limitations CoreLocation applies in this use case. If you set it up as you describe, the typical behavior will be that the app will detect the beacon in the background at an unknown distance (often the max range of around 50 meters), range in the background for about 5 secs, then the app will be suspended by iOS. That five seconds of ranging time will typically not be enough for the user to get near enough to the beacon to trigger your use case.
Unfortunately, there are no easy workarounds with standard beacons and CoreLocation. CLBeaconRegion objects do not have a distance field like CLCircularRegion does for geofences.
Two more extreme approaches you might try:
Turn off monitoring of your one region as soon as you detect the device is too far away, then re-enable it right as your app suspends itself in the applicationWillResignActive: callback. You might get a new entry event and more background ranging time.
Use nonstandard beacons that periodically stop their transmissions to trigger forced exit/entry events.

iBeacon region monitoring AND proximity for >20 beacons?

I have been working on a prototype iOS app utilizing iBeacons to provide location-relevant information to office employees depending on where in the office they are. The ideal use case is that whenever an employee enters or exits their office, a callback is fired which provides them some information in the form of a notification (it might make a server query to get information first, etc - that sort of thing). We also want to be able to do this when the app is backgrounded or terminated; fortunately, we already know that beacon region boundary crossings trigger the appropriate CoreLocation callbacks even if the app is backgrounded or suspended.
From looking around, I understand that broadly, I have two options for how to approach the beacon region monitoring:
Give each iBeacon its own CLBeaconRegion, and monitor for each of these regions independently.
Monitor for CLBeaconRegions that correspond to multiple iBeacons - for example, each iBeacon has the same UUID and only monitor for a CLBeaconRegion corresponding to that UUID - then try to determine which beacon triggered the boundary crossing using ranging.
Thus far, I had chosen option #1. The advantage of this approach is that I get didEnterRegion: and didExitRegion: calls for each individual beacon and immediately know which beacon I have entered/exited. Also, I only get one enter call and one exit call, which is exactly what I want. Unfortunately, I just realized that this approach also limits me to 20 beacons (since each beacon gets its own region).
I'm not as familiar with the exact implementation details of #2, so correct me if I'm wrong. But it seems that this approach has more drawbacks:
Apple discourages ranging when the app is in the background because the results may not be as accurate.
The ranging calls fire once every second, while I only want to have "enter/exit" callbacks.
If the beacons have region overlap, the ranging calls might continually flip which one is "closest", which would further complicate things.
Basically, I'm wondering if there is a way to utilize option #2, but still have the benefits of option #1 - a quick and easy way to immediately determine which beacon triggered the region change with only one enter or exit callback?
I hope this question is clear enough. It's not all entirely clear in my own head, especially how ranging works.
Option #2 is absolutely more complicated, but you must accept these complications in order to get around the 20 region monitoring limit.
A few points:
In the background, you only have around 5 seconds of ranging time, which does not give you as much time to average RSSI (signal strength) from each beacon to get a good distance estimate. So, yes, the estimates will be less accurate. If you understand this limitation and can live with it for your use case, there is nothing wrong with ranging in the background.
Yes, you will get multiple ranging calls per beacon after region entry, and you won't get any callbacks on region exit. You have to write extra code to take care of this. I have done this by maintaining a NSMutableArray of all the unique beacons (same uuid/major/minor) seen and update it in the ranging callback. You can then access this array in the region exit callback, so you know which beacons disappeared. Of course, it is possible that additional beacons were seen after the 5 seconds of background ranging time expires, but your app will never know about them. With this option, you must accept this limitation.
While it is true that errors on the distance estimate in ranging may incorrectly tell you which beacon is closest, you have an even worse problem when doing monitoring, because you don't get a distance estimate at all. If multiple beacons come into monitoring range around the same time, there is no guarantee that the first entered region callback you get will be for the closest beacon. So if your use case requires taking action based on the closest beacon, then you must do ranging (knowing that there may be error on the distance estimate.)
The drawback of the second approach is detecting the entry of a particular beacon will be purely based on ranging, that will not work if the application is killed. The reason is we will get didEnterRegion only once, because we are monitoring only one region with a particular UID. The next beacon with same UID will not be detected again if the application is terminated or if the background ranging stopped.
I recommend a combination of the mentioned approaches ,
Use same UID for all the beacons.
A beacon is uniquely identified using major/minor value that is collected when ranging.
As mentioned in apple doc, always keep number of monitoring regions below 20 by removing and adding beacons when the user moves from beacon to beacon (better to keep a beacon neighbour relationship graph in the server.)
Start ranging when entering the region ... and identify major/minor and calculate proximity.
Stop ranging when exiting the region.
Find the closest beacon from ranging method (need to skip unknown range beacons).
Monitor only the neighbours of the closest beacon in a given time.
When implementing both options, We should consider one fact, An iBeacon will be detected in 200feet distance. There may be multiple beacons in 200feet range.
If you use the same UUID for every beacon, you can just set the major/minor numbers to differentiate between the different beacons. This way, you are only monitoring for 1 beacon instead of > 20. Then just sort out which one is which from the other identifiers. This is how it works currently with Starbucks and other retailer apps. 1 beacon no matter where you are in the world, and different identifiers to sort things out on the back end.

Find a specific iBeacon after i entered the Region (Background mode)

This question is about when the Application is in background
I got a Application which works with 2 iBeacons (same UUID, same Major, different minor ).
The first iBeacon (minor 1) is for example for the Door. When the App is in the Background i get a Notification that i enter the region. This works fine. And now, for example, i walk in the Store and at some Point is the second iBeacon (minor 2).
If i'm in the immediate Zone of the second iBeacon, the Application should fire a notification. ("Hey you're at the cashpoint").
But i read that's not possible when the Applikation is in the Background. It only works when i hit the shoulder button or home button.
http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html
So the Question: Can anybody give me a hint how to implement this use case.
Several things:
When you create a beacon region, you can either match on just the UUID, the UUID and major value, or the UUID, major AND minor value.
If you specify just the UUID, your beacon enter/exit notices will not include major/minor value information.
Likewise, if you create a region with a UUID and major but not minor value, your beacon enter/exit messages will include the UUID of the beacon and the major, but not the minor, value.
The simplest thing to do is to create a separate region for reach unique beacon you're tracking. Then you'll get separate enter/exit notices for each beacon.
If you want multiple beacons to be part of the same region, but report different major or major/minor values, you have to turn on beacon ranging and wait for a ranging call. In the ranging call you'll get an array of beacon objects (not beacon region objects, CLBeacon objects) for all beacons that match the current region. The beacon objects will include the actual UUID, major, and minor for that beacon.
If you are in the background and have both beacon monitoring and ranging turned on, you'll get an didEnterRegion message (or maybe a changed state message) followed by several seconds of ranging information, and then your app will go back to sleep.
When your app is in the background, your only real option is to display a local notification and hope the user wakes your app again. If the user swipes that notice, the system prompts the user for unlock credentials if needed, and then takes the user back to your app.

Resources