I am working with two iOS devices, one is set up as an iBeacon.
I am ranging the beacon with a second iOS device and can grab its state via:
-(void)locationManager:(CLLocationManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(CLBeaconRegion *)region{}
When I stop advertising on the iBeacon, the location manager on the second device continues to range/recognize the iBeacon for 5-10 seconds.
Is this expected behaviour?
Does the peripheralManager take time to wind down?
I have a setup with dedicated BTLE hardware running as iBeacons.
Actually it takes 20-30s to stop calling locationManager:didRangeBeacons:inRegion: and after that it calls locationManager:didExitRegion:
In my understanding it makes sense and it's exactly what CL is looking for because the device needs some time to make sure it really exited the region.
On the other hand, it starts ranging for beacons almost instantly after you fire the beacon up. There's no reason to wait to start ranging.
If you inspect the proximity property of the beacon you will notice that it's CLProximityUnknown during that time. Maybe you can do something with this state to do what you need.
Related
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.
I need support for iBeacon Technology. In our case, I want one feature of continuously scanning (Ranging) of iBeacons weather our app is in foreground or background. We know its possible with CLLocationManager standard update location methods. But we don’t want to go with standard Location Updates (As we don’t want to take risk of battery drain). Is there any other option to start continuous Ranging of CLBeaconRegion without using CLLocationManager?
I am using Estimote Beacons and there are delegate methods or Estimate SDK(ESTBeaconManager class) available "https://github.com/Estimote/iOS-SDK", whenever beacons come in range or goes out of range,below said delegate methods will be called internally and help us to reduce battery drain.We can place the proximity checks in didEnterRegion Method:
- (void)beaconManager:(ESTBeaconManager *)manager didEnterRegion:(ESTBeaconRegion *)region{}
- (void)beaconManager:(ESTBeaconManager *)manager didExitRegion:(ESTBeaconRegion *)region{}
- (void)beaconConnectionDidSucceeded:(ESTBeacon*)beacon{}
- (void)beaconConnectionDidFail:(ESTBeacon*)beacon withError:(NSError*)error{}
- (void)beaconManager:(ESTBeaconManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(ESTBeaconRegion *)region{}
The only way you can detect iBeacon transmissions on iOS is with CoreLocation, and you are correct that constant ranging does drain the battery significantly.
In theory you could use CoreBluetooth to detect other beacon types like AltBeacon (reading iBeacon advertisement details are blocked by CoreBluetooth), but battery usage would still be similar.
The standard way to save battery in the background is to use CoreLocation beacon monitoring APIs, which are easier on the battery. Then when a beacon is detected by these monitoring APIs, you can start ranging, even if your app is in the background.
For more info on extending background ranging time, see here: http://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios.html
I am using iBeacon Technology in my application.When I open the app,beacon monitoring gets started and after that I kill the application and put the phone in sleep mode or lock the screen. If I go near to the beacon then sometimes app starts monitoring and sometimes not. Don't know what's the issue? I have set three flag for monitoring beacon region as below.
beaconRegion.notifyOnEntry = true;
beaconRegion.notifyOnExit = true;
beaconRegion.notifyEntryStateOnDisplay= true;
And When I press the lock button of the iphone. It starts monitoring for beacons and if I am in beacon region then it works perfectly..
Can anyone help me out ?
I have been working with iBeacon for around a year now, i have had the same scenarios encountered.
As per my experience with this if you are already inside a region, it takes some time to notify the 'didEnterRegion' delegate method. But if you are outside region a then entering the same you are likely to get the delegate called instantly and this depends on the Beacon Manufacture you are testing with. [More accuracy were found with RadBeacon, Estimote and Kontakt ]
Normally the TxPower configured to the beacons will be +4 dBm thats a Beacon can transmit till 70m/230". Try with going out of the region with the App in killed state and then enter the region.
When we set notifyOnEntry, notifyOnExit to YES/true, the control is with the OS LocationManager wether to notify the App about the region entry state, and you can handle it with a Notification thrown to the User and start Monitoring and then Ranging for the encountered region.
I want to implement a feature on iOS to notify user when reach a special location (such as a bus station, don't miss it), it is easy if user always put app in foreground mode, using GPS location to detect whether arrived nearly or not. The key point is how to achieve it in background mode.
One possible solution is to update GPS in background mode, something like map navigation APP, but as you know GPS is power killer, user may not like it.
See CoreLocation API, found the following delegate callbacks, I am not sure whether they work well in background mode. Any idea on this topic please share and discuss together and thanks in advance.
- (void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_TBD,__IPHONE_5_0);
From the Location and Maps Programming Guide
In iOS, regions associated with your app are tracked at all times,
including when the app isn’t running. If a region boundary is crossed
while an app isn’t running, that app is relaunched into the background
to handle the event. Similarly, if the app is suspended when the event
occurs, it’s woken up and given a short amount of time (around 10
seconds) to handle the event. When necessary, an app can request more
background execution time using the
beginBackgroundTaskWithExpirationHandler: method of the UIApplication
class.
I came across this tutorial (http://www.appcoda.com/ios7-programming-ibeacons-tutorial/) about iBeacon which I found interesting. I've downloaded their source code and able to run well.
Anyway, I noticed one strange thing. There are times, when I run the broadcaster first instead of the receiver, the receiver seems to not be able to detect any beacon but if I run the receiver first, only then the beacon can be detected.
I've tried looking into other tuts but iBeacon seems like a new technology and not much reference can be found yet. Can any of you guys who had get their hands dirty into this iBeacon thing shed some light on this intermittent issue?
Your help are much appreciated.
The issue you're seeing is because the receiver app only starts 'ranging' for beacons if it detects that you have entered a region. If you start the broadcaster first, you're already inside the beacon's region, so your app might not start ranging. This sentence in the article is the clue:
Launch the receiver app and carry it far away from the broadcasting beacon and then walk towards it to simulate entering the region.
Monitoring for a beacon means that your app will only be notified when you enter or exit a region you've defined. The radius of this region could be up to ~50m, so if you're just sat next to the receiver, you shouldn't trigger one of these events unless you turn the receiver off and on again. Monitoring can be done whilst the app is running in the background or the foreground.
Ranging for beacons in a region means that the app will be notified once per second with a list of all beacons that the device can see in the specified region (ordered by distance). Ranging will call the locationManager:didRangeBeacons:inRegion: method of your CLLocationManager's delegate. The list of beacons will be constantly updated as you move around inside / outside the region and your distance to the beacons changes. Ranging is intended to happen whilst your app is in the foreground.
If you would like the app to continuously listen for beacons, try calling
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
in your viewDidLoad:, instead of in your didEnterRegion: method.
Its due to current location delegate available methods. Add this method also -
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
This will fix the issue.