iOS iBeacon Monitoring triggered randomly - ios

I have a very strange behavior in my iOS application. While the beacon monitoring is working fine in most cases, there are sometimes hours where the didEnterRegion and didExitRegion events are fired multiple times in a row. The beacon itself is right next to the phone (about 15cm), so there shouldn't be an didExitRegion at all. I know that it is possible to loose a beacon signal what triggers an exit, but it's triggered about 5 times in 3 minutes (both enter and exit - so 10 calls). This is a very rare behavior and seems to be randomly.
The beacons are from Estimote and set via Estimote App to a broadcasting power of -20dBm what should be about 3.5 meters/ 12ft (according to Estimote App) and an advertising interval of 2000ms.
My init of CLLocationManager
if (! _locationManager) {
_locationManager = [[CLLocationManager alloc] init];
if ([_locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]) {
// Not available in iOS 8
_locationManager.allowsBackgroundLocationUpdates = YES;
}
// For iOS 8
if ([_locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.delegate = self;
}
That's how i start monitoring
- (void)startMonitoringForBeaconRegions {
for (CLBeaconRegion *currentBeaconRegion in _beaconRegions) {
//default should be YES
currentBeaconRegion.notifyOnEntry = YES;
currentBeaconRegion.notifyOnExit = YES;
[_locationManager startMonitoringForRegion:currentBeaconRegion];
}
}
Does anyone have a similar behavior with their beacons and maybe a solution ??
Kind regards,
Kyaak

I observed a very occasional similar behavior in my demo/test apps. I experimented with setting beacons to max Tx power and 100 ms advertising interval, and it didn't help, which leads me to believe it might be a bug in Core Location itself. I've seen this erratic behavior with both Estimote SDK (which builds on top of Core Location), but also in pure Core Location.
It ordinarily takes 30 seconds of not "hearing" a beacon for an exit event to trigger, but in situations like these, I was observing enters and exits happening in rapid succession, like it completely ignored the 30-second timer, so I believe this is not actually caused by iOS not being able to "hear" the beacon.
I've also experimented with monitoring for CLCircularRegion instead of CLBeaconRegion (so, a GPS geofence instead of a beacon geofence) set up around my apartment and office, and I've observed the same, sporadic, erratic enters and exits.
Unfortunately, I haven't managed to figure out a fix/workaround for that yet. Maybe if we collectively start opening bug reports with Apple, they'll look into that.

Related

Beacon Region Monitoring Get Delayed and randomly not working.(Android is working fine)

I have developed on application which is also developed in android.
I have implemented the beacon region monitoring in iOS as follows.
#pragma mark - Start/Stop Monitoring
- (void)startMonitoring {
[self clearRegionWatch]; // This function removes the already registered monitored regions
NSArray *arrayOfSavedBeacons = [self getSavedBeacons];
if([arrayOfSavedBeacons count]){
for(Beacons *beaconModel in arrayOfSavedBeacons) {
beaconModel.region.notifyOnEntry = YES;
beaconModel.region.notifyOnExit = YES;
beaconModel.region.notifyEntryStateOnDisplay = NO;
NSLog(#"Monitoring start request: %#", [beaconModel dictionaryRepresentation]);
[locationManager startMonitoringForRegion:beaconModel.region];
[locationManager requestStateForRegion:beaconModel.region];
}
}
else{
UIAlertView* curr1=[[UIAlertView alloc] initWithTitle:#"No Beacons" message:#"No Beacon List Found From Server" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[curr1 show];
}
}
Above is the code that starts the monitoring.
Following is the code that i have written for Initialization of Location Manager Instance.
locationManager = [[CLLocationManager alloc] init];
if([locationManager respondsToSelector:#selector(startMonitoringVisits)]) {
//iOS 8.0 onwards
[locationManager startMonitoringVisits];
}
if([locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
//iOS 9.0 onwards
locationManager.allowsBackgroundLocationUpdates = YES;
}
if([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
//iOS 8.0 onwards
[locationManager requestAlwaysAuthorization];
}
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager setDelegate:self];
[locationManager startUpdatingLocation];
Above code will initialise the location manager when app will start.
I want to get notified for the region entry and exit event.
My issue is my android app can detect the beacon entry from very long distance while iOS app can not detect the region enter or exit from far.
I don't know why this difference is coming?
What i have observed is beacon region monitoring sometime delayed the entry exit notifications by 2 to 3 minutes.
if android can detect the beacon region at particular range then why iOS app can not detect this?(it is significant difference in range form where both app can start to detect the app).
Any advice or suggestion would be helpful.
Thanks.
With iOS CoreLocation, the didEnterRegion monitoring callback is made upon first detection of a beacon matching the region regardless of the signal strength of the beacon advertisement. The callback should fire when the bluetooth chip first sees an advertisement at all, and this should be at a similar range for both iOS and Android devices. While the maximum bluetooth detection range certainly varies between devices, and can be affected by adding a case, putting your phone in your pocket, or by obstructions, in typical use it does not vary a great deal.
A more likely explanation why you are seeing delays in detections is timing, not signal strength. In the background, iOS will defer to a bluetooth chip hardware detection slot to quickly match your beacon region. This is a limited resource, so if these are exhausted, iOS will fall back to periodic software scans to detect, which can take up to 15 minutes. You can confirm this hypothesis by placing your iOS device at the same location where Android first detects, and simply waiting to see if it eventually tests at that distance.
A few tips to make your detections faster:
Uninstall all apps that may monitor beacons on your iOS device, as they may be using up the limited hardware acceleration slots. (Approx. 30 across all apps on the device.)
Don't stop monitoring and restart unless you absolutely have to. This will put your app last in line to get a hardware acceleration detection slot.
Start ranging for beacons when you start monitoring. This won't affect background detection times, but will significantly speed up foreground detection times.
If your app is in the background while you are ranging, it can take up to 15 minutes to get region notification of enter and exists. If your app is running in the foreground ,you should get entered region notifications within a second, and exit region notifications within a few seconds.
It is not beaconSpecific issue but the way of CoreLocation API is implemented in iOS.

location manager - only getting 1 update per second

I'm timing speed runs in cars. I'm using the usual location manager code.
viewDidLoad
CLLocationManager *locMgr;
if (locMgr == nil)
locMgr = [[CLLocationManager alloc] init];
[locMgr setDelegate:self];
[locMgr requestWhenInUseAuthorization];
[locMgr setDistanceFilter:kCLDistanceFilterNone];
[locMgr setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locMgr startUpdatingLocation];
DELEGATE
- (void)locationManager:(CLLocationManager *) manager didUpdateLocations:(NSArray<CLLocation *> *) locations
{
(process update)
}
QUESTION
Even though I'm using the most precise DistanceFilter and Desired Accuracy, I'm only getting one update per second while the car is moving, even at 60 mph. I checked the number of locations returned to the delegate every second and it's always one. Can anyone explain this? It seems to me that I should be getting a constant stream of updates at 60 mph. Any help would be greatly appreciated.
Thank you.
I'm new to iOS development as I'm just starting to port my speedometer app from Android, and this question came up in my search for locations update rate. Hopefully it's ok that I can't answer the question but merely add info I've learned from Android.
So, iOS is probably different but Android sends out location updates whenever the (for example) GPS service gets a new location, and from experience 99.99% of Android phones and tablets have sensors that send updates once per second. If sensors of the same quality and energy efficiency are used by Apple (idk) then I'd expect 1Hz updates are the best we'll get.
My Android app can use Bluetooth GPS receivers at ~10Hz so I want my app to report the latest location whenever it's received, so timers won't really work for me.
Your question has helped me as I now know when location is updated.

iBeacon monitoring and ranging in background

I took several months developing an application based on iBeacons and I'm really frustrated.
The general idea is that when a beacon is detected, the user is notified with information specific to that iBeacon.
The application was designed as follows, all iBeacons have the same UUID, the Major determines the building (Museum, shop...) and the Minor the specific product (pictures, shoes...). So this application may serve multiple clients.
When the application starts, I begin to do monitoring and ranging for region with our UUID. When the app is in foreground all works perfect. But in background or suspended state the problems starts. Ranging is not allowed on background or suspended state.
I know that the app will be launched into the background for about 5 seconds when you enter or exit the region of a beacon. You can here do ranging in the background for this five second period, after which iOS will suspend your app again.
I managed to extend ranging for up to 3 minutes in the background with the technique learned here. I also get an extra callback with notifyEntryStateOnDisplay = YES;
But this is not enough, if a client enters a region with the app in background or suspended state, he will be notified. And during the extra 3 minutes he will be notified if the ranging detects another iBeacon, but when the 3 minutes background task expired, if no region exit is triggered he will not receive any notification again.
Is there no real solution in a scenario like this? I think it is a very common scenario and I'm surprised no way to deal with it.
EDITED: I tried to find a solution to the problem by monitoring two regions as recommended David Young in his response. In order to obtain more events of entry/exit regions.
I added the code I implemented to try to monitor two regions.
but something I have done incorrectly and didRangeBeacons:InRegion: callback is firing every 10 ms when the expected is every second.
On AppDelegate.m, I'm doing the following inside didFinishLaunchingWithOptions:
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
[self.locationManager startMonitoringForRegion:self.beaconRegion2];
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion2];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion2];
Then, on didRangeBeacons:InRegion:
- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
if(beacons.count > 0){
[self.locationManager stopRangingBeaconsInRegion:region];
for (CLBeacon *beacon in beacons){
NSLog(#"beacon detected major: %# minor: %#", beacon.major,beacon.minor);
}
[self.locationManager startRangingBeaconsInRegion:region];
}
}
When I run the application on the simulator, and there is a beacon of each network in range, the message is displayed on the console approximately every 10 ms.
I suspect that the stop and restart the ranging is breaking the expected callback flow, but when only one region is in the range, callbacks occur every second as expected.
The problem you describe is common. The easiest approach is to extend background ranging time forever using a background location mode. (This is similar to what the OP has already done, but for an unlimited period of time.)
Two other options:
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.
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.
Beacon ranging in the background works just fine on iOS 8, provided you jump through all the hoops.
Firstly, you need the correct authorization. Add the key NSLocationAlwaysUsageDescription to your Info.plist, and call
[self.locationManager requestAlwaysAuthorization];
Your app must have a Background Mode of Location Updates. Specify in Info.plist or via the Capabilities (sits parallel alongside Build Settings).
Then check for authorisation in applicationDidBecomeActive with
switch([CLLocationManager authorizationStatus])
{
...
Once authorised, you need to do:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// If you don't do this, location updates will pause after 15 minutes in the same place
// Usually this is what you want, so I've left this line commented out
// self.locationManager.pausesLocationUpdatesAutomatically = NO;
// Create a NSUUID with the same UUID as the broadcasting beacon
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:kProximityUUID];
_beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"uk.co.airsource.testregion"];
// Necessary to get continued background ranging.
// See https://community.estimote.com/hc/en-us/articles/203914068-Is-it-possible-to-use-beacon-ranging-in-the-background-
_locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[_locationManager startUpdatingLocation];
[_locationManager startMonitoringForRegion:self.beaconRegion];

ios deferred location updates fail to defer

I am looking into using deferred location updates for an iOS activity tracker, which allows location services in background. I've implemented the suggested code snippets (see below). In Xcode debugging, deferred locations attempt to start a few times until location data comes in at about 1 per second. After that, it claims to succeed in starting deferrals, and the callback for the finish trigger also succeeds after the specified time period expires. However during the time, the location handler still runs once per second. I've read that this is because the phone hasn't deemed itself ready to enter the background, and that testing in Xcode does this. Note, AppDelegate's "didEnterBackground" eventhandler got called immediately when turning off the screen, and resumed when reopening app.
I ran the same code with the phone disconnected as another test, near the window with GPS, screen off, or switching to entirely different apps, and it still never actually defers the updates. I can tell because the networked update still comes in once every 30 seconds, instead of the interval of 120 seconds which is desired in the code sample below.
What else is needed to actually get deferrals to work, since there is no error occurring in starting them and they do get their finish callback? Why do location updates continue at 1 per second even when the app goes to background?
Iphone 5s, IOS 7.1.1
// .h file (partial)
#interface MotionTracker : NSObject<CLLocationManagerDelegate, UIAccelerometerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
// .m file (parial)
- (id) init {
if(self = [super init]){
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// if set to YES (default), app stops logging location at some point and doesn't resume in any timely fashion, all data points lost in between
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeFitness;
}
return self;
}
// called early in program after login confirmed
- (void) startCollectingLocation {
[_locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// logs to file when device is not in debug
// always returns 1
NSLog(#"Location update count: %d",[locations count]);
// some code here to handle location updates
// - collect key location day in NSDictionary
// - every N seconds send Network call to server to save (have tried 30 seconds, 15 minutes, 30 minute network intervals). Have also tried turning off network calls completely.
// deferred updates starter
if (!self.deferringUpdates) {
if([CLLocationManager deferredLocationUpdatesAvailable]){
[_locationManager allowDeferredLocationUpdatesUntilTraveled:500 timeout:(NSTimeInterval)120]; // (have also tried large numbers, and "Infinite"
self.deferringUpdates = YES;
NSLog(#"Deferred updates start");
} else {
NSLog(#"Deferred updates not available");
}
}
}
- (void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
if(!error){
_deferringUpdates = NO;
NSLog(#"Deferred updates: finished");
} else {
_deferringUpdates = NO;
NSLog(#"Deferred updates: %#", [error localizedDescription]);
}
}
If the device is connected to a debugger or on a charger, the device will remain powered (not sleep) and therefore will not enter deferred mode. Deferred mode is a power optimization allowing the device to sleep. If the device is not scheduled to sleep for other reasons, enabling deferred mode will not force it to sleep otherwise. Try your test by ensuring no other apps are using location services, and disconnecting it from a charger with the screen off. After running for some time, plug back in and check your logs, you should see that the device slept and deferred updates.
From Apple's
allowDeferredLocationUpdatesUntilTraveled:timeout: documentation:
Deferred updates are delivered only when the system enters a low power
state. Deferred updates do not occur during debugging because Xcode
prevents your app from sleeping and thus prevents the system from
entering that low power state.
It is also worth noting that deferred updates are only available when locationManager.desiredAccuracy is set to kCLLocationAccuracyBest OR kCLLocationAccuracyBest; locationManager.distanceFilter must also be set to kCLDistanceFilterNone.
From Apple's documentation:
...the location manager allows deferred updates only when GPS hardware is available on the device and when the desired accuracy is set to kCLLocationAccuracyBest or kCLLocationAccuracyBestForNavigation.
and
...the distanceFilter property of the location manager must be set to kCLDistanceFilterNone.
I have been struggling with the same issue, and I may have found an answer that solves this problem for many - at least it solves my problem and gets deferred updates working consistently for me.
I followed all of the steps in this list and no matter what I did, location updates would not defer. It occurred to me that I might have other apps running that were not allowing the system to sleep, so I killed all other apps in the multitasking tray. I ran my sample app again and ... it worked!
But the story doesn't end there. I tried again a little later, and even though there were no other apps running in the multitasking tray I couldn't get location services to defer. Then it occurred to me that I have an app on my phone called "Moves" which manages to keep itself alive even after you manually kill it. I'm not entirely sure how Moves comes magically back to life when you kill it, but it does (perhaps using bluetooth and the app preservation / restoration service). Even though it is alive and tracking your location it doesn't appear in the multitasking tray. I think that only apps that are manually launched appear in the tray - if the OS launches an app it doesn't appear in the tray. But I digress ...
I was able to get deferred location services to work consistently in my app by disallowing Moves to use location services. When I did, Moves complained even though it wasn't in multitasking tray. It seems that if another app is using location services (and not deferring) your app won't defer either.
Hi recently with the iOS 9 GM seed version out,I have seen location update(allowDeferredLocationUpdatesUntilTraveled:timeout:) not getting deferred.The same code used to work in iOS 8.4 and below versions.Its draining my device's battery by a huge margin.
Is there anything we need to explicitly set or mention for iOS 9?Didn't find anything from Apple documentation
Here is the code that I implemented.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (!self.deferringUpdates) {
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:30];
self.deferringUpdates = YES;
} }
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { // Stop deferring updates self.deferringUpdates = NO;
}
I also set the allowsBackgroundLocationUpdates property but even that didn't help. self.locationManager.allowsBackgroundLocationUpdates=YES;
In iOS 9 and later, regardless of deployment target, you must also set the allowsBackgroundLocationUpdatesproperty of the location manager object to YES in order to receive background location updates. By default, this property is NO, and it should remain this way until a time when your app actively requires background location updates.
https://developer.apple.com/library/prerelease/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html
Please let me know what additional I need to make
Thanks

iBeacons ranging in background sometimes has delay

I have an iBeacons app able to range for beacons while in background or not running. I implemented UILocalNotifications and they work fine, meaning i get a notification when i reach the range of a beacon.
Not having a real beacon i created an app (for another device, let's say an iPad for the next scenario) that acts like 2 different beacons, meaning it can broadcast 2 different signals, same UUID but different Major/Minor values (call this beacon A and B), one at a time obviously. My problem is in this scenario:
Have my iPhone (with iBeacons app closed) in lock screen
Activate my iPad app, broadcasting beacon A
My iPhone reacts showing me a notification
I stop iPad app from broadcasting beacon A, wait 1 second, start broadcasting beacon B
My iPhone DOESN'T react
I stop iPad from broadcasting
A few minutes later (about 2) my iPhone shows me the notification of beacon B
Now what i don't understand is this delay, the first time my iPhone reacts immediately, the second time it takes about 2 minutes to notify me the beacon.
If, after beacon B notification, i re-start broadcasting a beacon (A or B) my iPhone reacts immediately, then for the next time it always waits for 2 minutes.
Why is this happening? I've read some article saying that it's because the bluetooth awakes every 2-4 minutes while the app is in background, so i can get the info not faster than this time. But i don't see much sense in this because whenever i get the second notification the broadcasting of the beacon (B in my scenario) was already stopped, it means that if the bluetooth awakes in that very moment no beacon was in the air! But i get the notification, so it means that in someway my iPhone found it before i stopped the broadcasting.
Is this a problem that can be solved?
EDIT with some code
Here is my viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize location manager and set ourselves as the delegate and beacons dictionary
_beacons = [[NSMutableDictionary alloc] init];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Create a NSUUID with the same UUID as the broadcasting beacon
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"6C1AA496-1653-403D-BD1E-7F630AA6F254"];
// Setup a new region with that UUID and same identifier as the broadcasting beacon
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"testregion"];
NSLog(#"startMonitoring");
// Tell location manager to start monitoring for the beacon region
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
_myBeaconRegion.notifyEntryStateOnDisplay = YES;
// Check if beacon monitoring is available for this device
if (![CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Monitoring not available" message:nil delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil]; [alert show]; return;
}
}
Now whenever i get a beacon i send a notification, i just wanted to try how it worked so i didn't implemented yet a way to send just 1 notification, it means i get about 9 notification, 1 per sec that's the active time the code can run while in background i suppose (1 second enter region, 9 ranging for beacons)
-(void)locationManager:(CLLocationManager*)manager
didRangeBeacons:(NSArray*)beacons
inRegion:(CLBeaconRegion*)region
{
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground){
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Found Beacon";
notification.soundName = #"Default";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
}
Actually to be more specific, if i totally close my app from the multitasking view, or just let it in background, as soon as i start broadcasting a beacon i get the notification**S** (1 second delay). Then stopping the broadcasting and re-play the delay becomes in the order of minutes.
Now for a real scenario, where i should have many beacons in the same place, this delay could be a problem, as long as i get the notification when i could be already far away from the beacons itself.
Has my code any problem? I read those articles but i never found a delay of 15 minutes.
EDIT2 after davidgyoung suggestions
I modified my code and as you said using 2 different regions for beacon A and B the delay is always null. I also logged with the piece of code you gave and i discovered this.
Broadcast the beacon of Region_1
Device shows me the notification of Region_1
Stop broadcasting the beacon of Region_1
The logs say i'm still in the region, only after a couple of minutes i get the log "OUTSIDE Region_1", just now i can re-play the broadcasting to get another notification from Region_1.
So i was curious about this and i read and article http://beekn.net/2014/03/apple-ios-7-1-launches-major-ibeacon-improvement/
The author says that from iOS 7.1 the responsiveness of exiting a region is immediate, actually i'm running 7.1 but i also have a couple of minutes delay. Why this? Did you find the same problem in your tests?
Now, i read that a device can listen for no more than 20 regions right?it means that if i have 100 beacons i can set up just 20 regions and divide these 100 in 20 groups, getting no more than 20 notification (assuming these 100 are in the same place, all in the range of my device) ? That could be a problem because will force the user to run the app in the foreground to get all the information (assuming again each of the 100 beacons have a particular and unit role), am i right?
EDIT: This is my first answer before seeing the code.
I cannot explain this behavior based on your description, but I suspect there may be an issue with the setup that is either delaying your local notification or inaccurately reporting the state of beacon region B when the notification is sent.
Two things would help verify/eliminate this possible cause:
Add NSLog statements in your didDetermineState: forRegion: callback like below, then repeat your test reporting the log results.
// put this in your AppDelegate
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if(state == CLRegionStateInside) {
NSLog(#"locationManager didDetermineState INSIDE for %#", region.identifier);
}
else if(state == CLRegionStateOutside) {
NSLog(#"locationManager didDetermineState OUTSIDE for %#", region.identifier);
}
else {
NSLog(#"locationManager didDetermineState OTHER for %#", region.identifier);
}
}
Post the code that sets up monitoring and issues the notification on detection.
If you have not read this already, you may want to give a quick look to similar tests I have done to measure background detection times:
http://developer.radiusnetworks.com/2013/11/13/ibeacon-monitoring-in-the-background-and-foreground.html
http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html
After seeing the code, I think a few issues are going on here:
The code defines only a single region that encompasses BOTH beacon A and beacon B, preventing independent region entry/exit monitoring callbacks, and stopping the phone from getting woken up when switching from transmitting beacon A to beacon B. This is critical, because this means that if you switch from transmitting beacon A to beacon B, iOS will consider itself still in the one region. This can prevent the phone from getting a monitoring event that will wake up the phone in the background.
There is no code in the ranging callback to check which beacon was visible, so it does not seem possible to know for sure which beacon caused the notification. The notification will fire even if the beacon array passed to the ranging callback method is empty (i.e. if no beacons are detected.)
Ranging generally does not work in the background. The only exception is for a few seconds right after entering/exiting a region. I suspect that the two minute delay you report is the time it takes for your iPhone to perform the next background scan for iBeacons (This delay can be 2, 4, or 15 minutes depending on phone state). After that next next scan is performed, a region transition is detected (probably an exit region notification because nothing is transmitting), and ranging starts up for 5 sec firing a notification.
This is all very difficult to test and troubleshoot without directly logging the of the monitoring region callbacks as mentioned in my first answer as well as the ranging callbacks, and logging the identifiers of the beacons detected. Make sure you understand which of these callbacks are firing in order for which beacons, and don't try to troubleshoot too much at once!
In addition to troubleshooting the individual callbacks first, I would also recommend:
Changing the code to have one region per beacon for maximum background responsiveness.
Putting the detected beacon identifiers in the notification so you know beacon caused the notification to fire.
DavidGYoung is correct. iOS7.1 will invoke your app when terminated upon sensing a beacon region traversal. But,and this is critical, iOS will ONLY invoke a terminated app when you ENTER or EXIT a beacon region, and only if your app has previously registered for location events for that particular beacon region.
In your explanation above, it looks like you are only monitoring for ONE beacon region (you did not specify Major/minor numbers so any beacon with the same UUID will qualify). Unfortunately you created two beacon transmitters with the same UUIDs. So when you started the beacon you got ONE notification (ENTER) but had to wait a couple minutes for another (EXIT). typically I've seen exit notifications about 30 sec after a beacon stops transmitting.
If you want to perform the experiment again, try registering for TWO different beacon regions (declare two regions with diff Major/Minor numbers, or make two diff UUIDs), and then use these two diff values in your beacon transmitter. Should work then.
Tom

Resources