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
Related
I was trying to understand the difference between Beacon Ranging and BLE Scanning. From what I understand, beacon ranging uses BLE scanning to find the beacons. On top of that it calculates proximity of beacons using the signal strength(which I do not need). I just need to detect the beacons(similar to beacon monitoring). I am not using beacon monitoring because of the Always permission requirement in IOS. I know that beacon monitoring is highly optimised as compared to ranging, but I wanted to know how ranging compares to BLE scanning.
Can i use BLEModule.scanForDevices(UUID) in place of CLLocationManager.startRangingBeacons(region) to detect beacons? If yes, can I get callbacks in similar fashion when beacon is detected?
Are there any downsides of doing this in terms of battery performance or detection time, etc.?
Note that I am willing to make do without proximity information(distance from beacon).
Also, a related question:
Can beacon monitoring be used in foreground with WhenInUse permission in iOS?(I found mixed views on this in my initial investigation)
You cannot use Core Bluetooth scanning to detect an iBeacon. You must use Core Location and monitor a CLBeaconRegion to discover iBeacons.
Having discovered a beacon you do not need to range it.
As stated in Apple's documentation
Important
Apps must have always authorization to use region monitoring, and they must be configured with the Location updates background mode to be launched.
Even if you only want beacon notification when your app is in the foreground you must request always permission.
There is a lot of confusion regarding the restrictions that are applied by the iOS on apps that want to scan BLE beacons\peripherals.
After reading several blogs and Stack Overflow answers, I want to see if I understand all the issues correctly. Please correct me if there is anything I misunderstood or missed. I refer only to iOS 7 and above, and focus on detection and not connection (Can you connect to a CLBeacon using the iBeacon Monitoring & Ranging API?).
The options for the beacons are clear - Use a general purpose BLE peripheral or use a BLE peripheral that advertises in the iBeacon format (Also, a non-standard peripheral can advertise in the iBeacon format in the adv-packet and a different format in the scan-response packet).
General Restrictions
iBeacon Ranging will let you know which beacons are around you. You must specify the ProximityUUID that the beacons advertise beforehand (no "general" scanning). didRangeBeacons will be called every second with an array of CLBeacon objects that were found recently. The distance from the beacon and its accuracy are calculated by the iOS using some confidential algorithm that only Apple's developers really know (The algorithm is based on the rssi values and the rssi-at-1-meter calibration byte that the beacon advertises). You can also use iBeacon Monitoring to call a delegate every time you enter or exit a region - again you must specify the ProximityUUID that you are looking for (you can also specify a major & minor). "Exiting a region" is defined by some time of not receiving any advertisement, and therefore cannot be immediate. The number of regions that can be ranged\monitored simultaneously per device is limited to 20 - This means that if other apps do monitoring\ranging at the same time, your app may not be able to monitor\range (right?).
CoreBluetooth - You can also detect other ad-structures in the beacon's advertisement. If the beacon advertises in iBeacon format too, you cannot see the iBeacon fields (ProximityUUID, major, minor...), despite the fact that they are sent under a standard "Manufacturer Specific" ad-structure that you can see in other cases.
Running in the Foreground - The less restricted use-case:
iBeacon Ranging and Monitoring - no further restrictions.
CoreBluetooth - Passing nil in the serviceUUIDs of scanForPeripheralsWithServices will scan for all peripherals. Passing CBCentralManagerScanOptionAllowDuplicatesKey as YES in the options will make the didDiscoverPeripheral to be called multiple times for the same peripheral\beacon (I assume that using a timer you detect the advertisement was not received for some time and assume that the user exited the "region").
Running in the Background - The more restricted use-case:
iBeacon Ranging will not work directly. iBeacon Monitoring will call didEnterRegion and give the app runtime of 6 seconds - in which you can start Ranging (for example, to detect major & minor). The detection may not be immediate since iOS turns scanning on and off to preserve the battery power. If you enter a region of multiple beacons with the same ProximityUUID, and you monitor this UUID without a specific major and\or minor, didEnterRegion will be called when you start receiving the signal from the first beacon - however, if you did not exit the region of the first beacon and you also entered the region of a second beacon the app will not be woken up again (didEnterRegion will not be called again) so you cannot start ranging to detect the second beacon's major & minor. The app cannot simply pop up to the foreground, but can create local notifications and other background operations.
CoreBluetooth - According to Core Bluetooth Background Processing scanForPeripheralsWithServices can run in the background using, but you must specify at least one serviceUUID. didDiscoverPeripheral will be given a runtime of 10 seconds. Using CBCentralManagerScanOptionAllowDuplicatesKey will not work - didDiscoverPeripheral will be called once for every peripheral. Therefore, you cannot detect "exit" from the region and "re-entry". I suppose you can use a non-standard BLE peripheral that changes its MAC address to overcome this issue. The app cannot simply pop up to the foreground, but can create local notifications and other background operations. The detection may not be immediate since iOS turns scanning on and off to preserve the battery power.
Running after the app is killed
iBeacon Monitoring - Works! Even if the user killed the app or the device was restarted.
CoreBluetooth - The app will be woken up if it was killed by the iOS (due to inactivity or memory constraints). However, if the user explicitly killed the app it won't be woken up (which makes the first case hard to test). I don't know what happens after a device restart...
Does anyone have more experience with these restrictions? Can scanForPeripheralsWithServices be used as a better alternative to iBeacon Monitoring in some use-cases?
Thanks!
You are mostly right with your description. Just two clarifications:
The 20 region limit is not per device, it is app-specific. No matter what other apps are doing on the mobile device, your app is still allowed to monitor up to 20 regions by iOS. That said, there are likely hardware limits that are device-specific on how many regions can be monitored in the background with hardware assistance. These limits are undocumented. If you surpass these undocumented limits, it will probably take a lot longer to detected beacons in the background. (Although that said, there is no OS guarantee of when the detections come, anyway.)
You cannot connect to a CLBeacon using Monitoring and Ranging APIs. These APIs only work with BLE advertising packets, which are connectionless.
Yes, it is possible to use scanForPeripheralsWithServices as an alternative. This is what Gimbal beacons do in order to implement a proprietary system. There are real disadvantages, however, in terms of background detection time and reliability.
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.
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.