Do beacons work like geo fences and have the same limit of 20? - ios

I read somewhere, that beacons on apple, work in a similar way as geo fences, and there too is a limit of 20 per phone.
Is this the case?
Our use case: We want to send push notifications to users when the app is not running and the phone is not awake (in the pocket). We are thinking of relying on beacons instead of geo fences in order to avoid hitting the limit of 20 fences per phone and having to deal with significant location change function and similar solutions (for now).

Beacons work similar as geofences. You will get notified when entering or exiting a beacon-region (in bluetooth range) and you can also range all beacons around to get their ids.
There is also for CLBeaconRegions the 20 regions limit. See:
https://developer.apple.com/reference/corelocation/cllocationmanager/1423656-startmonitoringforregion
With a combination of monitoring regions and ranging, you could be able to do your stuff even with one region. (Depends heavily on your use case)
If you want to send geo-based notifications, beacons could work for you.

Yes there is a limit of max 20 regions (both beacon and geofence combine).
There are ways you can monitor 1000s of beacons but it depends on the use case.
You can use different wildcards and monitor 1000s of beacons but with some limitations. You have to play with beacon configurations like beacon UDID, major and minor values.
If you are only interested in enter or exit of any of the beacons (and not interested in specific beacon). You can actually just set the same UDID of the all the beacons and only monitor one beacon region will solve your problem.
let region = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "YOUR_BEACONS_UDID_32_DIGITS")!, identifier: "beacon_region1")
locationManager.startMonitoring(for: region)
You can also group the beacons with UDID and Major value.
let r1 = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "YOUR_BEACONS_UDID_32_DIGITS")!, major: 1000, identifier: "region_1000")
So you can monitor 20 groups of beacons in background and each group can have as many beacons as you want.

Although CoreLocation APIs limit beacon region monitoring to 20 regions (combined with geofence regions), you can look for nearly 86 billion different beacon locations for the purpose of sending notifications to a user.
Yes, that's right. 86 billion -- 85,899,345,920 to be precise. Think that's enough?
The simple technique is to combine beacon monitoring APIs with beacon ranging APIs. Using monitoring, you can wake up your app in the background when any one of your beacons is detected. Then using ranging APIs you can read the exact beacon identifiers detected, and send a push notification to the user. This will all work with the phone still sitting in the user's pocket.
Because each beacon identifier includes a major (0-63355) and a minor (0-63335), you can monitor for the maximum 20 beacon regions with a different ProximityUUID, and get 20 x 65536 x 65536 = 85,899,345,920 different combinations.
let region = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6")!, identifier: "beacon_region1")
locationManager.startMonitoring(for: region)
locationManager.startRangingBeacons(in: region)
// TODO: repeat the above for up to 19 more regions
...
public func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
for beacon in beacons {
var uuid = beacon.proximityUUID
var major = beacon.major
var minor = beacon.minor
// TODO: Send a unique local notification to the user for this
// uuid/major/minor combination identifying a particular place
}
}
There are some limits to this approach. If your beacons are placed within overlapping radio range of each other (~ 50 meters) then a user dwelling by one beacon for awhile then walking to the one next door will not get a background wakeup unless the beacons are in two different regions. There are some games you can play to get around this limitation, but for many use cases you don't even need to bother.

Related

Change beacon sending frequency in AltBeacon

I am using the AltBeacon API to send beacons in an Android app. I am using the startRangingBeacon method with the deafult region as an argument -
region = Region("all-beacons", null, null, null).
I know that beacons are sent with the frequency of 1 second. Is it possible to change this frequency?
I tried to search for a frequency variable in the BeaconManager but could not find one.
Simply call:
beaconManager.scanPeriod = 2000L // set callback to 2000ms instead of the default 1100ms
Be aware that increasing the frequency of callbacks will sometimes mean you don’t get detections, as some beacons do not advertise more frequently than once per second. If no detections are made, the callback will have a beacon count of zero.

Swift: didExitRegion delay causes missed beacon events

I am monitoring for two beacons defined as beaconRegion1 (Room 1) and beaconRegion2 (Room 2), and I am logging enter and exit events for each (because we want to calculate time-spent-in-room).
I am experiencing a challenge in logging the exits because of the slight delay that occurs when didExitRegion fires.
Real World:
Time 0:00 (minutes:seconds): Person enters Room 1.
Time 10:00: Person exits Room 1.
Time 10:15: Person enters Room 2.
Time 20:00: Person exits Room 2.
Swift/iOS World:
Time 0:00: Person enters Room 1, didEnterRegion fires for beaconRegion1 (Room 1)
Time 10:00: Person exits Room 1. Crickets chirp.
Time 10:15: Person enters Room 2, didEnterRegion fires for beaconRegion2 (Room 2)
Time 10:30: didExitRegion belatedly fires for beaconRegion1 (Room 1)
Time 20:00 Person exits Room 2, but no exit is detected because Exit 1 was delayed by ~30 seconds and therefore programmatically occurred after the Room 2 entry was triggered.
In short, the delay allows the second entry to precede the first exit and so the second exit never triggers.
I understand the didExitRegion delay is unavoidable. I'm curious if there are workarounds to catch the second exit in this example, perhaps by delaying when didEnterRegion fires to allow the first exit to "catch up" or perhaps by running separate didEnter and didExit functions for each beaconRegion? Swift seems intent on forcing me to use CLRegion or CLBeaconRegion as the region: parameter for didEnterRegion and didExitRegion.
Code Example:
let beaconRegion1 = CLBeaconRegion(uuid: UUID(uuidString: "...")!, major: 12345, minor: 12345, identifier: "Room1")
let beaconRegion2 = CLBeaconRegion(uuid: UUID(uuidString: "...")!, major: 23456, minor: 23456, identifier: "Room2")
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
startScanning()
}
}
}
func startScanning() {
locationManager?.startMonitoring(for: beaconRegion1)
locationManager?.startMonitoring(for: beaconRegion2)
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
guard region is CLBeaconRegion else {return}
print("Enter: \(region.identifer)")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
guard region is CLBeaconRegion else {return}
print("Exit: \(region.identifer)")
}
As you said yourself, the delay before a region exit is unavoidable. The way iOS detects a region exit is that ~20 seconds has passed since the last time a beacon packet was detected.
The job of your computer program is to adapt these beacon events to meaningful real-world events. There are countless ways to do this, and which solutions are appropriate totally depend on the real world events ("use cases") you are trying to solve with your program.
Based on your description, it sounds like you want to know when the user moves the phone from one room to the next. There are many ways to do this, and which one is best depends on the "requirements" of our system -- how far apart are the rooms? How quickly do users move from one room to the next? Where are the beacons placed within the rooms? Can you ever see two beacons from two rooms at the same time? The proper algorithm needed to solve your use case depends on the specific requirements.
Two ideas:
If the rooms are far apart and beacon visibility will not overlap: Ignore region exits. Determine which room you are in based on which region entry happened last. If you ever get a region exit for the same room your program thinks it is already in, then your program should determine it is not in any room at all.
If the rooms are close enough that beacon visibility overlaps: Don't use beacon monitoring at all. Instead use beacon ranging, which gives you an update every second with a list of all visible beacon along with an estimated distance in meters (beacon.accuracy). The room you are in is determined by which beacon is closest. When using this approach, you probably also want to add some "debounce" to the closest beacon determination to keep from bouncing back and forth with the noise on the distance estimate. A simple "debounce" algorithm would be to require that a second beacon be at least 10% closer than the currently tracked room beacon before it will be considered the closest. You can adjust this percentage to get the best tradeoff between bouncing and time lag to determine the next room.
Simplified and solved based on replies from #Paulw11 and #davidgyoung. Thanks. If I use a single didDetermineState as opposed to separate didEnterRegion and didExitRegion methods, it correctly tracks all enter and exit events for each region regardless of the order of enter and exit events.
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
guard region is CLBeaconRegion else {return}
print("Event detected: \(state.rawValue) for \(region.identifier)")
Of note to other swift novices like me: state.rawValue here returns 0 for unknown, 1 for inside region, and 2 for outside region. region.identifier returns the identifier string (in my case, either Room1 or Room2 since I am monitoring for two beacon regions.

iBeacon monitoring multiple beacons with same UUID and different major, minor

I have multiple iBeacons with the same UUID but different major and minor numbers. It can be different combinations for major and minor, but the UUID stays the same. Say for example,
device #1 - UUID xyz, major 1, minor 1
device #2 - UUID xyz, major 1, minor 2
device #3 - UUID xyz, major 2, minor 1
.....
device #n - UUID xyz, major 10, minor 1
Also, these iBeacons are located in close proximities, ranging from 1 - 50 feet. Therefore, their regions can intersect with each other.
In my iOS app, I want to detect all the iBeacons with the same UUID in the area and then iterate through them and read major and minor of each to detect which one of them have been detected.
Can I use
init(proximityUUID: UUID,
identifier: String) with my UUID and then iterate through them ?
There are two different iOS CoreLocation APIs and you'll need to use the first one for this purpose:
1. Beacon Ranging (What you want)
You call locationManager.startRangingBeacons(in: region) with a region definition that leaves major and minor nil. The constructor you show init(proximityUUID: UUID,
identifier: String) does exactly that.
This will give you a callback to locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) one time per second with an array of all CLBeacon objects that match your region definition. You can iterate over this array to see all of them.
2. Beacon Monitoring (What you do not want)
You call locationManager.startMonitoring(region: region) with a region definition that leaves major and minor nil.
This will give you a callback to locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) or the equivalent didExitRegion method each time at least one beacon matching the region definition appears, or all the beacons matching the region disappear.
This will not let you iterate over all matching beacons as the callback only includes the region definition not the list of matched beacons.
Yes you definitely can! You will not get new delegate notifications when another iBeacon is detected unless you create multiple listeners with different IDs but once you are in the Region of a given UUID, you can iterate through all beacons within range and get their major and minor

iBeacons changes to the framework in iOS 10.x

Swift 3.0 iOS 10.x
I am revisiting the world of iBeacons after initially looking at the technology, but seem to have come across a significant change in the way the framework seems to work in iOS 10.x; although maybe I am simply losing my mind.
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if beacons.count == 0 {
return
}
This method as I understood it previously returned an array of beacons.
Which it still does, only it returns an array with a single element only each time. Sure it continues to find all the beacons within range, but fails to scope them all when scanning returning as soon as it finds one it would appear?
Is there a parameter I need set here or something perhaps?
When ranging beacons with two different ProximityUUIDs, you must set up two different CLBeaconRegion objects for each ProximityUUID, then start ranging for each.
You will get one ranging callback per second for each CLBeaconRegion you are ranging. And the callback will have a reference to both the CLBeaconRegion and an array of the CLBeacon objects detected for that region.
Because you have one beacon per region, what you are seeing where there is on beacon per callback is expected. This is how the API has worked since iOS 7.

Does the location manager's limit of 20 regions mean 20 total geofence AND beacon regions?

I am afraid I know the answer to this.
Apple's location manager docs for the startMonitoringForRegion method say that you can monitor a maximum of 20 regions at a time.
I have a client app that makes heavy use of both geofence regions and beacon regions.
I had assumed that there were separate 20 region limits for geofence regions and beacon regions, but I fear that the limit is actually 20 regions total for both types.
Can somebody confirm my fears based on actual experience?
Yes, the 20 region limit is the maximum that CoreLocation lets you monitor for both CLBeaconRegions and CLCircularRegions (geofences) combined. When iOS 7 added beacon support, beacon regions inherited this same limitation for geofences because of the ways the APIs were defined. And as you suspected, the limit applies to any type of region you want to monitor. So you can monitor 10 CLBeaconRegions and 10 CLCircularRegions but no more than 20 combined of each type.
The limit is 20 for each type of CLRegion. This means you can monitor 20 CLCircularRegion and 20 CLBeaconRegion.
This isn't documented but I did some tests and added more than 20 CLBeaconRegion and more than 20 CLCircularRegion. The results were that self.locationManager.monitoredRegions.count was 40 having 20 monitoredRegions of type CLCircularRegion and the other 20 of type CLBeaconRegion.
Once you reach the limit for each type of CLRegion and you try to monitor new regions (of that type) they get ignored and locationManager:monitoringDidFailForRegion:withError: is called with the kCLErrorRegionMonitoringFailure error code.

Resources