I make an application with iBeacon.
And I decide to use CoreLocation and Ranging.
But, I think Ranging use too much energy.
As an alternative to Ranging, I tried to use Monitoring.
Then, I profile Ranging's usage energy level and Monitoring's usage energy level.
Ranging use about 2 energy usage levels more than Monitoring
Result: (Ranging's level: 10/20, Monitoring's level: 8/20)
But, Monitoring doesn't called didExit or didDetermineState immediately.
I want My app has a Real-Time as Ranging.
My Solution:
- Monitoring Start
- If I enter Monitoring region, I start Ranging
- If I exit region, ranging result is nil, and stop Ranging
- Monitoring Stop and Start.
Monitoring doesn't called exit or determineState method immediately.
But, I discovered stop and rerun monitoring make my application has Real-Time.
I think that solution reduce energy usage when standby.
And It Actually Working!
▼ Here is my Code.
class Service: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, CLLocationManagerDelegate{
private let constraint = CLBeaconIdentityConstraint(uuid: Constants.beaconUUID!,
major: Constants.beaconMajor,
minor: Constants.beaconMinor)
private let region = CLBeaconRegion(beaconIdentityConstraint:
CLBeaconIdentityConstraint(uuid: Constants.beaconUUID!,
major: Constants.beaconMajor,
minor: Constants.beaconMinor),
identifier: Constants.beaconIdentifier)
var locationManager: CLLocationManager!
override init() {
super.init()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
}
}
extension Service {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if manager.authorizationStatus == .authorizedAlways {
region.notifyOnExit = true
region.notifyOnEntry = true
region.notifyEntryStateOnDisplay = true
manager.startMonitoring(for: region)
}
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == .inside {
didEnterEvents(manager)
}
}
func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
if beacons.first == nil {
didExitEvents(manager)
stopAndRerunMonitoring(manager, for: region)
}
}
}
extension Service {
private func stopAndRerunMonitoring(_ manager: CLLocationManager, for region: CLRegion) {
print("reboot!")
manager.stopMonitoring(for: region)
manager.startMonitoring(for: region)
}
private func didEnterEvents(_ manager: CLLocationManager) {
print("inside")
manager.startRangingBeacons(satisfying: constraint)
}
private func didExitEvents(_ manager: CLLocationManager) {
print("outside")
manager.stopRangingBeacons(satisfying: constraint)
}
}
I know my solution is terrible.
But I can't find any other solution.
Plz, Can you find any other better solution?
etc:
- didEnter and didExit called determineState method.
I need to call requestState() method in other code. so I write a Enter event logic in determineState method. - You need Real-Time?
Yes, because i will make safety system with beacon. So My application's requirements has Real-Time. I need Real-Time regioning and less energy usage.
CodePudding user response:
The approach you describe of starting ranging on region entry and stopping it on region exit is not "terrible". It is in fact quite common. It is often used to determine the exact identifiers of the beacons detected, which would otherwise not be possible by monitoring alone.
For your use case, it is also true that ranging while inside a region will expedite the region exit event, because it forces a constant Bluetooth scan rather than relying on the slower hardware filters used for monitoring. While ranging is active, region exits come 30 seconds after the beacon was last detected.
For this to be a practical solution, you must take into account background behavior:
- Unless you do special tricks, iOS won't let you range for more than a few seconds when the screen is off. As soon as ranging stops, you lose the expedited region exits.
- While the screen is on, there is no point in saving battery by not ranging all the time, because the illuminated screen uses over 100x as much battery as the constant Bluetooth scan of ranging.
To get any benefit from the technique you describe, you must extend the ranging time in the background as described in my blog post here. Be aware that since I wrote that article, Apple has reduced the time you can extend ranging in the background from 180 seconds to 30 seconds, which may not be enough for your needs. You can get unlimited background ranging by:
- Add the location background mode to Info.plist
- Obtain "always" location permission from the user. It is not sufficient to get "when in use" permission.
- Start a background task as described here: http://www.davidgyoungtech.com/2014/11/13/extending-background-ranging-on-ios
- Request 3km location updates from CoreLocation. This will keep the app running in the background without extra battery drain from GPS, as 3km accuracy only uses the cell radio You don't need to do anything with these results. You just need to request them to keep the app alive.