I am implementing the IOS application with swift 3.0 which can monitor nearby beacons. I have an issue relating didRangeBeacon function, Detail reproduces steps below:
1- Application has entered beacon region
2- beacon is turned off
3- In next 10 seconds, didRangeBeacon function is still called and response still has one beacon object.
So I can not recognize beacon is off immediately. How can I recognize beacon is off immediately?
In case of iBeacon, the area is defined by the range of one or more beacons. This allows more granularity and precision than regular geofencing—the latter being based on a mix of signals from cell towers, WiFi and GPS. Beacon geofences are also more responsive: “enter” events usually take up to a few seconds to trigger, “exit” events up to 30 seconds. (Regular geofences take “3 to 5 minutes on average”, according to the CLLocationManager’s documentation.)
Testing “enter” events involves making sure you’re “outside” the region. Enabling “flip to sleep” makes it easy to simulate going out of range. “Enter” events take up to a few seconds to trigger, while “exit” events take up to 30 seconds.
Region monitoring is pretty slow, you can use it for more general notification to let you know. You can use this signal strength to decide if you no longer see the beacon (CoreLocation tends to still "see" the beacon a few seconds after it disapears).
Related
I have an app where I'm scanning for beacons ( I know the UIIDs) and displaying them. The first few times I ran it I found the beacons almost immediately. But now each time I run it, it seems to take longer and longer to detect them. Even though I have the beacons sitting on the desk right next to the device.
I've tried replacing the batteries, changing from a iPhone to an iPad, checking a beacon scanning app on the Mac, rebuilding the app, etc. I also have a range of different beacons from different manufacturers and they all seem to have the same issue. Nothing seems to fix this.
My code basically looks like this:
let locationManager = CLLocationManager()
locationManager.delegate = self
let region = CLBeaconRegion(proximityUUID: UUID(uuidString:"61687109-905F-4436-91F8-E602F514C96D")!, identifier: "BlueCat beacon")
locationManager.startMonitoring(for: region)
And then I can wait for anything up to 5 or more minutes before any of the delegate methods get call.
This is the first time I've attempted to use beacons. Is this normal? Can it take a long time for a beacon to be seen by a device?
Under ideal conditions, didEnterRegion callbacks on iOS come within a few seconds of a beacon coming into range. Under less ideal conditions, these callbacks can take 15 minutes or more.
There are lots of things that can cause delays in detecting beacons when monitoring, and it's hard to say for sure without knowing exactly how you are testing. Two tips:
Hardware filter slots on iOS devices are limited and are on a first-come, first-served basis. If you have a bunch of beacon apps installed that have taken up these slots (or if your app has registered a bunch of regions), all these slots may have been taken up. Once hardware filter slots are gone, detections fall back to software scans that happen about every 15 minutes. Uninstall all apps (including yours) that may be holding on to these slots, then reinstall just your app.
Make sure you aren't "in region" when you put the app to the background. If iOS believes it is in the region, you won't get a new didEnterRegion callback until after it realizes it left the region and then entered again. The time for iOS to realize it exited the region is much longer than the time it takes to realize it entered. To ensure it realizes it exited the region, you should always range for beacons when the app is in the foreground, and leave the app ranging in the foreground for about a minute to ensure iOS realizes it is no longer in region (it usually takes 30 seconds of ranging to trigger an exit.) Only then should you put it in the background and turn on a beacon to measure detection times.
I have this iBeacon application in swift that perform some task when didExitRegion and didEnterRegion are called. Everything works well except I am getting false alert in a way that I am within the beacon range and all the sudden didExitRegion is getting fired and immediately in a few secs later didEnterRegion getting fired again. This happens when either the phone or the beacon hasn't moved or anything blocked the signals.
I read somewhere that this is a bug on the Apple side. Not sure how true this is, but what's the work around this?
I was thinking to get the Alt, Lat every time the didExit is getting fired and compared with with the previous values or if there is a better way of doing this?
Thanks
I think a potential solution could be to move the code currently inside of your didExitRegion method into a new new method. In didExitRegion start a timer for 5 seconds (or whatever you find to work best) inside to call the new method when it fires.
Then, if didEnterRegion is called while the timer is running, cancel the timer and return from didEnterRegion.
From my experience, it seems this is something that happens when your device loses the beacon's signal momentarily while still within range. My apps have sent me didEnterRegion notifications every 5 minutes when I've been sitting right next to the beacon. From what I've read, the performance of the app depends on the quality of the beacon as far as signal strength and signal frequency.
This is a problem with beacons that do not transmit frequently enough. A didExitRegion event will fire on iOS if no beacon packets have been detected in 30 seconds. If a beacon is nearby and transmitting at the recommended 10 Hz, false positives are very, very rare.
Many beacon manufacturers reduce transmission rates to save battery, some making transmissions happen as seldom as every 5 seconds. This leads to bad performance and false exits. Not all beacon packets transmitted get received due to radio noise and interference. In practice, only 80-90% do. With a beacon transmitting every 5 seconds, 6 missed packets in a row will cause an exit event. This usually will not happen, but sometimes it will.
The best solution is to configure the beacon to transmit more often, ideally at 10 Hz or more. If you get a beacon that lets you do this and make it transmit that often, false exits will be very, very rare.
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.
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.
Take this scenario: user has an iPhone in the pocket passing by an iBeacon. Her phone is region monitoring for it. How much time is needed in real world to from entering the region to the moment app is woken up?
I have found an excellent article on the subject by David G. Young (http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html) but I can't believe it can really take up to 15 minutes.
That would mean that all iBeacon home automation scenarios are simply unusable because you won't neither wait 15 mitutes in dark room for lights to switch on neither you would switch on the lights by actively using an app. Door opening and locking is another situation where iBeacon would be unusable (and NFC would work much better here). Or am I missing something?
I have an app that ranges for an iBeacons. It first grabs a list of couple proximityUUIDs and registers them as monitored regions.
When the device enters that region, it takes just 1 to several seconds (iPhone 5S) to post me a local notification on didEnterRegion: event.
When the app is in front, it starts ranging the beacon immediately in current region (if any) and updates happen in about one-second intervals.
When the app is in background, ranging is not enabled, otherwise it would immediately report that the beacon is gone (if you leave its range).
HOWEVER, It can really take up to 15 minutes (I've experienced this) for the device to post the didExitRegion: when in background in the worst case, when there is just significant location monitoring enabled combined with bad or no network. Otherwise it happens until about a minute.
Sorry, this didn't fit into a comment.