Once you have enabled beacon ranging in the background (see below), you check for beacons with proximity Immediate
in your implementation of the delegate function locationManager(_:didRangeBeacons:inRegion)
.
func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
let immediateBeacons = beacons.filter() { $0.proximity == .Immediate }
if !immediateBeacons.isEmpty {
let tappedBeacon = immediateBeacons.first!
// Your code for processing tappedBeacon ...
}
}
Beacons have Immediate
proximity if the phone is less than 30cm away (at default transmission power). If you reduce the transmission power even more, you can get it down to 5-10cm. tappedBeacon
in the code above is the beacon that you tapped with your phone or that you are nearly touching with your phone.
That was the easy part. Keeping beacon ranging alive in the background is a bit trickier.
First, you must enable "Location updates" under "Background Modes" in the "Capabilities" section of your project settings. This results in the following entry in the Info.plist file of your project:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>audio</string>
</array>
Second, you need to initialise an CLLocationManager
object in the right way:
func initLocating() {
if CLLocationManager.isRangingAvailable() {
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.delegate = self
}
}
This function is typically called from the init()
function of your class deriving from CLLocationManagerDelegate
. If your device supports ranging at all, this function requests authorisation for location updates. Then, it sets the desiredAccuracy
to kCLLocationAccuracyThreeKilometers
, which switches off any location methods other than cell tower triangulation. This is not required for background updates but it makes the battery of your phone live longer.
For background ranging, it is essential to allow background updates by allowsBackgroundLocationUpdates = true
. The final line of the function simply makes this class the CLLocationManagerDelegate
, which triggers the function locationManager(_:didRangeBeacons:inRegion)
whenever there is a change in the visible beacons or their attributes.
Third, you must start location updates whenever you need them. This could be for the whole lifetime of your app or triggered by a certain condition.
func startLocating() {
self.locationManager.startUpdatingLocation()
self.locationManager.startRangingBeaconsInRegion(CLBeaconRegion(proximityUUID: self.proximityID, identifier: "AllBeacons"))
}
The first call starts location updates in general. Without this call, beacon ranging in the background will stop eventually. The second call starts beacon ranging specifically for the beacon region you are interested in. It triggers the execution of locationManager(_:didRangeBeacons:inRegion)
.
If you can stop ranging beacons for some time, you can use the following function. It is just the inverse of startLocating()
.
func stopLocating() {
self.locationManager.stopRangingBeaconsInRegion(CLBeaconRegion(proximityUUID: self.proximityID, identifier: "AllBeacons"))
self.locationManager.stopUpdatingLocation()
}
Stopping and starting beacon ranging only when needed is a measure to increase the battery life of your phone.
I have described beacon ranging in the background in detail in a recent post. The above is a shortened version of my post.