When iOS 7 was announced in 2013, one of the items on the list of new API was iBeacon. The iBeacon API became part of the CoreLocation Framework by adding to the CLLocationManager class something called “Beacon Ranging”. Just like region monitoring based on location, known as GeoFencing, Apple also uses an iBeacon (or iBeacons for multiple beacons) as a region, and thus, when you come in to range of an iBeacon your iPhone can detect this and respond accordingly.
If you understand what an iBeacon is and what to do with them, you might want to jump the initial part of this post as the tutorial is further down the page. Click here to do so.
This tutorial is based on the previous iBeacon tutorial for Objective-C that I wrote in 2013. This new tutorial is based on the newer Swift programming language (version 3 at the time of writing).
What is an iBeacon?
An iBeacon is a bluetooth transmitter that emits a signal at a number of times per second (often 100ms or 10 times per second). It uses BLE (Bluetooth Low Energy) which means that a small battery, such as a coin cell, can power a device for a month or more. Using larger batteries the charge can last a year or longer.
The iBeacon is represented in iOS as a CLBeacon which has 3 properties to identify the iBeacon and another 3 to get information about the beacon. These properties are:
Identification
1. proximityUUID
2. major
3. minor
ProximityUUID is a unique identifier that you randomly generate for your business. An example of this could be a supermarket deploying 1000’s of beacons, but each of these would use the same UUID. This UUID does not need to be registered with Apple; you just randomly generate it when you create your app and then program each of your beacons with it. I will explain a little more about proximityUUID and how it relates to “region monitoring” later on in the tutorial.
Major is an NSNumber that ranges from 1 – 65,535. Major is one way you can group beacons together. One example could be that you own a 3 floor building. You might have a major of 1 assigned to all iBecaons on the first floor, a major of 2 for all beacons on the second floor, etc… Alternatively a large supermarket chain might assign major 1 to one store and number 2 to a different store.
Minor is also an NSNumber that has the same range as Major. Minor allows even more fine control over how beacons are assigned. You might think of these assigned out to departments on a floor such as minor 0 – 10 being the clothing section.
Combining major and minor together you have 65,535 x 65,535 combinations of iBeacon per proximity UUID which means that for every unique ID there are 4,294,836,225 combinations. Feel free to calculate how many UUID’s are available if you desire.
Note that you configure each physical iBeacon with the same UUID, and then assign a unique major and minor value to it. Ideally you wouldn’t have 2 iBeacons with the same major/minor values. The CLBeacon class on iOS is used to report back what beacon it came in to range of. When a beacon is in range a CLBeacon object is created and you can get the beacon details by inspecting the properties and then act accordingly with your code.
The next 3 properties are as follows:
1. proximity
2. accuracy
3. rssi
Proximity is described by 4 constants which are:
enum CLProximity : Int { case Unknown // An unknown proximity. case Immediate // Very close, often when the phone is within a few CM of the beacon. case Near // From a few CM to perhaps a number of feet away. case Far // From the end of near to out of range. }
It is difficult to put exact numbers on the above responses due to various factors such as RF interference, but they can give a good idea of how far you are away from a beacon.
Accuracy works in a similar way to how the accuracy of a CLLocation reports back; it returns a double which represents how accurate in meters the proximity detection is.
RSSI is the received signal strength of the beacon. It is returned as an Int. The further you move away from the beacon, the lower the number gets. The number is a negative value which starts around -35 and approaches almost -100 before dropping out of range.
A few Things about CLBeacon
Now that I have given a basic run down of what an iBeacon is and what CLBeacon is, the main things to take away from this are that you need a proximityUUID which is unique to your business. You randomly generate this from the terminal on a Mac (by typing uuidgen). It is almost a certainty that this command will not generate the same UUID twice, so run the command, and stick to that. I just ran it now and it generated. I will be using this for this tutorial, but you are free to use your own generated UUID. If you do, please ensure that you make that change in code:
E06F95E4-FCFC-42C6-B4F8-F6BAE87EA1A0
For how to organise the major and minor values for each beacon or set of beacons, you just need to use your own logic for this. You could go by floor, city, country, etc… or you could assign them out randomly. Just ensure that the software you track these in (even if a spreadsheet) is logically ordered so that you don’t have the same major and minor on a beacon in one location and the same on another location. Also watch out for clashes when you might have the following situation:
Beacon 1:
Major 675
Minor 23
Beacon 2:
Major 67
Minor 523
Although different, in some cases you might join these together to do a search for 67523 in your backend. If you do this, then which beacon are you in range of; 1 or 2? So when programming the app and working with the backend, it would be more ideal to have the backend with major and minor separated and not concatenated.
iBeacons Tutorial for Swift
Now that we have some of the basics of iBeacons out of the way, lets get started on the iBeacon tutorial. Working with iBeacons requires an iPhone 4S or newer, an iPad 3 or newer, an iPod touch (5th gen) or newer, or an iPad mini. If you don’t have an iBeacon, either of these devices can also act as an iBeacon transmitter so if you have 2 compatible devices you can transmit from one and receive from the other. Alternatively, you can buy iBeacons from eBay and other places such as Estimote.
The demo app we create will act as a transmitter or receiver and be very similar to the Objective C tutorial from a few years ago. In fact, I will be using the same layout and labels as the previous tutorial.
Create a New Project
The first step is to create a new project. You can follow this tutorial. Make sure you select Swift as the language. Feel free to call your project whatever you want. I will call mine iBeacon. The organisation name can be whatever you want for this project. I use my company name.
Storyboard
For this project we will start by creating the storyboard just as seen below:
The first View is embedded in a Navigation Controller by selecting the View and clicking Editor > Embed In > Navigation Controller. The view will move to the right and a Navigation Controller will be put next to it. The Navigation Controller will automatically become the Initial View Controller (as seen by the small arrow pointing to the left side of the NVC).
Connect up the Track Beacon UIButton to a new view by holding down CTRL and then click and drag from the button to the view on the top right that you added. Likewise, do the same for the other button and connect it to the bottom right view that was added.
Add all of the labels to the view.
The next step is to create the classes responsible for each of the views. The first view already has a class assigned called ViewController.swift which means we just need to add two more classes and assign them to their respective view controllers.
Click File > New > File (or cmd+N) and select Cocoa Touch Class. Click Next. Give it a name, in this case we can use Track. Select UIViewController as the subclass. The name will become TrackViewController automatically. Make sure that Swift is selected as the language.
Click Next and then select the correct folder for the file to go in. This usually defaults to the correct location, but sometimes on the first run it will put the file in the root of the project. It needs to go in the same folder as ViewController.swift, AppDelegate.swift and a few other items. Click Create. Your new class will be added to the project.
Repeat the last step but call it Transmit which will become TransmitViewController.
Go back to the Storyboard and select the top right view (the one for Tracking). Select the Identity Inspector in the right hand column and under Class it will have a greyed out UIViewController. With the dropdown, select TrackViewController.
Repeat again but select the bottom right view controller and then in the identity inspector, select TransmitViewController as the class. What we have done here is link the view to a class. That class we specify will contain the code responsible for how that view responds.
IBOutlet and UILabels
We added several UILabel objects to our storyboard. The next step is to wire up these properties to our class so that the Swift code we write can modify; or manage; the properties of the UILabel objects. We do this by using the familiar ctrl+click+drag. This is more easily done if you use the assistant editor (middle button at the top right of Xcode) so that we can see both the storyboard and the associated class for the view selected. The class manages objects on the view and thus, we need the connections building so that we can manage the objects by setting the text.
Select the label on the TrackViewController in the storyboard (the one next to iBeacon Found) and hold down ctrl, left click and hold and then drag the blue line across to the ViewController code on the assistant editor.
Next, type in a name for the IBOutlet. In this example I used iBeaconFoundLabel. Click connect.
You can repeat this process of ctrl+click+drag for each label, or you can copy/paste the following code and then left click and hold the small circle that appears in the left margin and drag each IBOutlet to the appropriate UILabel. Either way is ok.
@IBOutlet weak var iBeaconFoundLabel: UILabel! @IBOutlet weak var proximityUUIDLabel: UILabel! @IBOutlet weak var majorLabel: UILabel! @IBOutlet weak var minorLabel: UILabel! @IBOutlet weak var accuracyLabel: UILabel! @IBOutlet weak var distanceLabel: UILabel! @IBOutlet weak var rssiLabel: UILabel!
With the UILabel IBOutlets in place for the TrackViewController, we now need to connect up the outlets to TransmitViewController.
The following can be used:
@IBOutlet weak var uuidLabel: UILabel! @IBOutlet weak var majorLabel: UILabel! @IBOutlet weak var minorLabel: UILabel! @IBOutlet weak var identityLabel: UILabel!
One last part of preparing the storyboard is to add an IBAction for the Transmit button on the Transmit view. An IBAction is the way that the storyboard lets the code know that something happened. In the case of a UIButton, it was probably tapped which triggered the IBAction. We connect this up in a similar way to how we connect up the IBOutlet by ctrl+click+dragging to the Swift class associated with the view.
When we ctrl+click+drag, drag to just above the didReceiveMemoryWarning function. When release the click a box will appear. One thing to note is that a UIButton can have both an IBAction and an IBOutlet. The action is what happens when the user interacts whereas the IBOutlet is what is used to adjust the attributes of the button such as changing the text, what type of tap to look out for and modify other properties. In this tutorial we won’t need to use an IBOutlet for this button, so just select Action from the Connection type, and then set the options as follows (many of which are default):
When done, click Connect. Your TransmitViewController Swift class will now have the following:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func transmitButtonTapped(_ sender: UIButton) { } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. }
Note that this function does not start with override. The reason is that we are not overriding any method from the UIViewController class.
Each time you tap the Transmit button, the transmitButtonTapped function will be called. This is how we will interact with the app.
You might notice that we haven’t yet touched the ViewController.swift class. All we have done is add 2 UIButtons and ctrl+dragged and connected them to each respective view. This is all that needs to be done. There is no code that we need to write for this particular view. The standard Navigation Controller can handle what we need to do without us having to make any adjustments.
Transmitting an iBeacon from a Compatible Device
To benefit those that might not have a physical iBeacon, this tutorial will show you how you can use a second iOS device and use it as a transmitter. For those that do not have a second compatible iOS device, you’ll either need to find one or purchase an iBeacon from eBay.
For this part of the tutorial we will work in the TransmitViewController.swift file.
Transmitting an iBeacon
We first need to import 2 frameworks in to the TransmitViewController class. The first is CoreLocation and the second is CoreBluetooth. To do this, just below import UIKit add the following lines:
import CoreLocation import CoreBluetooth
The reason we need to import both of these frameworks is that we need to create a CLBeaconRegion and then need to transmit that via Bluetooth.
The next task is to adopt protocol from the CoreBluetooth framework. The delegate that we will adopt is called CBPeripheralManagerDelegate which is responsible for informing the app when the state of Bluetooth has changed (i.e., it powered on or powered off, or is unavailable). To adopt we add CBPeripheralManagerDelegate to the class declaration as follows:
class TransmitViewController: UIViewController, CBPeripheralManagerDelegate {
This particular protocol has 1 required method which we will implement now. You can put this anywhere between the class declaration after the { and anywhere before the closing } for the class. I just put this method above the didReceiveMemoryWarning method.
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { }
Now that this is implemented, each time the peripheralManager state changes, this method is called. At the moment it won’t do anything though because we haven’t created the peripheralManager object and also haven’t set the delegate. Lets do that now:
Just below the @IBOutlets at the top of the class, add a few properties:
var beaconRegion : CLBeaconRegion! var beaconPeripheralData : NSDictionary! var peripheralManager : CBPeripheralManager!
Although we are working on sorting the peripheralManager (line 3) we may as well use this opportunity to declare some other variables we need.
Line 1 we are declaring a CLBeaconRegion to use later.
Line 2 declares an NSDictionary that we store data in to advertise. We will use peripheralData(withMeasuredPower:) to create this later on.
Line 3 we have the CBPeripheralManager. This is the object that manages published services. A better explanation can be found in the API Reference for this particular class.
In the transmitButtonTapped IBAction that we created lets initialise the CBPeripheralManager and set the delegate. We do this with the following:
@IBAction func transmitButtonTapped(_ sender: UIButton) peripheralManager = CBPeripheralManager.init(delegate: self, queue: nil) }
Here we initialise the object and set the delegate to self, meaning that any delegate methods used will be triggered within this class.
Our next task is to prepare the beaconPeripheralData. In this example we will do this just prior to initialising the peripheralManager. Add the following line of code just above the initialisation of the CBPeripheralManager so you end up with the following:
@IBAction func transmitButtonTapped(_ sender: UIButton) { beaconPeripheralData = beaconRegion .peripheralData(withMeasuredPower: nil) peripheralManager = CBPeripheralManager.init(delegate: self, queue: nil) }
The “nil” for withMeasuredPower means that we will use the default value. If this was used for production you would likely want to put a value in there. The instructions in the API reference indicate that you should put the transmitter 1 meter away from the receiver and measure the RSSI. This value at 1 meter away represents the measured strength. For this simple app, we won’t bother setting this value and just accept the default.
Advertising the iBeacon
Earlier on we implemented the required delegate method for CBPeripheralManagerDelegate. We will now add some code to it as follows. This is fairly boilerplate code although on each condition you can choose to warn the user or handle some events quietly in the background.
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { if (peripheral.state == .poweredOn) { peripheralManager .startAdvertising(beaconPeripheralData as? [String : Any]) print("Powered On") } else { peripheralManager .stopAdvertising() print("Not Powered On, or some other error") } }
This delegate method provides a CBPeripheralManager object called peripheral. We use this to check the status of the peripheral.
First on line 2, we check the .state property of the peripheral. This is the CBPeripheralState class that provides 4 responses. First is .poweredOn which means we are ready to go.
If this is true we can start advertising on line 2 by calling the .startAdvertising method from the peripheralManager and passing in the beaconPeripheralData. We declare as? [String : Any] to inform the compiler what our intentions are and what to expect.
If there was some other message other than .powerOn, we handle that differently under the other condition. Normally we would check for other statuses, but in this example we won’t go that in to depth. If Bluetooth is off, the system will inform you and let you know that you need to switch it on.
For the remaining part of this class we just need to set the labels and also create the beaconRegion so that the class knows what bluetooth data to transmit.
I created the following:
func initBeaconRegion() { beaconRegion = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "E06F95E4-FCFC-42C6-B4F8-F6BAE87EA1A0")!, major: 1233, minor: 45, identifier: "com.devfright.myRegion") }
This initialises a CLBeaconRegion with the UUID string as mentioned earlier in this post. It also assigns a major and a minor value. I just randomly chose 2 numbers between 1 and 65,535 for each. I then gave the region an identifier. You can suit this to your own needs. If you are detecting more than 1 region the identifier can be useful so you can inspect what region you are dealing with. In this tutorial, we don’t really need to know what the identifier is although we still need to set one.
In viewDidLoad I called this function:
initBeaconRegion()
I then have a final function which sets the labels on the view as follows:
func setLabels() { uuidLabel.text = beaconRegion.proximityUUID.uuidString majorLabel.text = beaconRegion.major?.stringValue minorLabel.text = beaconRegion.minor?.stringValue identityLabel.text = beaconRegion.identifier }
Like with the initBeaconRegion function, we also need to call this from viewDidLoad:
setLabels()
This completes the transmit side of the tutorial. If you want to test now, load up the app on your compatible iOS device, make sure bluetooth is enabled, and hit Transmit iBeacon and then hit Transmit.
You won’t really see anything here other than the logs confirming a poweredOn message, but if you have an app installed that can detect beacons then set it to listen out for the proximityUUID provided above and when you hit transmit you will see the other iOS device detect it.
Lets now make the receiver side of this app.
Detecting an iBeacon with Swift
Switch to TrackViewController.swift. In here you should have 7 properties as @IBOutlets that we created earlier when setting up the storyboard. You should also have 2 methods (a viewDidLoad and a didReceiveMemoryWarning).
The first task we will handle in this class is importing the needed CoreLocation framework and also adopt and implement some of the methods from the CLLocationManagerDelegate.
Start by importing the CoreLocation framework. Note that we do not need the CoreBluetooth framework now as CoreLocation handles all of that in the background.
import CoreLocation
When added, adopt the CLLocationManagerDelegate as follows:
class TrackViewController: UIViewController, CLLocationManagerDelegate {
There are no required methods in the CLLocationManagerDelegate although we will implement one method that allows us to inspect the iBeacon(s) as they come in to range.
Add this method:
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { }
This method is called about once per second and provides an array of beacons that are currently in range including the ones that have just gone out of range (for a few seconds). We will use this method to set the labels on the view to show which iBeacon is in range.
If you want to only detect when a beacon comes in to range and exits, there are other delegate methods you can use although these only provide the region and not the actual iBeacon major and minor of which triggered the didEnter/didExitRegion. Normally you would use those as well as ranging.
Add the following to the above method:
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { let beacon = beacons.last if beacons.count > 0 { iBeaconFoundLabel.text = "Yes" proximityUUIDLabel.text = beacon?.proximityUUID.uuidString majorLabel.text = beacon?.major.stringValue minorLabel.text = beacon?.minor.stringValue accuracyLabel.text = String(describing: beacon?.accuracy) if beacon?.proximity == CLProximity.unknown { distanceLabel.text = "Unknown Proximity" } else if beacon?.proximity == CLProximity.immediate { distanceLabel.text = "Immediate Proximity" } else if beacon?.proximity == CLProximity.near { distanceLabel.text = "Near Proximity" } else if beacon?.proximity == CLProximity.far { distanceLabel.text = "Far Proximity" } rssiLabel.text = String(describing: beacon?.rssi) } else { iBeaconFoundLabel.text = "No" proximityUUIDLabel.text = "" majorLabel.text = "" minorLabel.text = "" accuracyLabel.text = "" distanceLabel.text = "" rssiLabel.text = "" } print("Ranging") }
On line 2 we will just extract the last object in the array (i.e., the last CLBeacon). In this example we will only detect one iBeacon so the last should also be the first, but if you did switch on 2 iBeacons with the correct proximity UUID then the closest one to you physically (i.e., the highest RSSI) will be first in the array.
On the next line we check if there are any iBeacons in range. If so, we extract the data from the beacon in range such as the major/minor and other details and we set the text property of all the UILabels we have as IBOutlets.
If there are no iBeacons in range, we set each label to an empty string except we change the top label to a “No” string.
Now that this function is complete, we now need to start the iBeacon services to listen out correctly. We will first do this by initialising the locationManager, setting the delegate, and then requesting authorisation to use location services.
In viewDidLoad add the following:
locationManager = CLLocationManager.init() locationManager.delegate = self locationManager.requestWhenInUseAuthorization()()
On line 1 we initialise the location manager and store that in locationManager.
Line 2 we set the delegate to self (meaning that any delegate methods implemented will be triggered here).
On line 3 we request when in use authorisation which means we are prompting the user to ask for permission to use location services on the device only when the app is being used. However, we cannot request authorisation without declaring an entry in the info.plist file for the app. Open the info.plist file and add a new row. You need to add the following key:
NSLocationWhenInUseUsageDescription
When added, you need to enter a description. This is what is displayed to the user when they are prompted with the request to access location data. For this I just used the following string “Used to scan for iBeacons.”
Although not needed in this tutorial, another setting to consider is found in the Capabilities section of the app. If you want the app to continue scanning for iBeacons when the app is closed (even terminated/suspended etc…) then you can enable this in Capabilities under the Background Modes section. Switch that on, enable Location Updates, and then add the NSLocationAlwaysUsageDescription key and instead of using locationManager.requestWhenInUseAuthorization(), use locationManager.requestAlwaysAuthorization().
To bring all this together we just need a couple more functions adding to the class and then to call them from viewDidLoad to kick off the scanning.
Add the following function:
func getBeaconRegion() -> CLBeaconRegion { let beaconRegion = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "E06F95E4-FCFC-42C6-B4F8-F6BAE87EA1A0")!, identifier: "com.devfright.myRegion") return beaconRegion }
This function creates a beaconRegion. Unlike the transmit class where we specified a major and minor value, in this section we just specify UUID and an identifier. This allows the app to detect any major/minor values for the specified proximity UUID. If we wanted to only scan for beacons with a particular major value then we can initialise and specify a major value. If a beacon with a matching UUID switches on but the major doesn’t match, then it will be ignored.
When the beaconRegion is created we then return the beaconRegion to the caller.
The final function to add is as follows:
func startScanningForBeaconRegion(beaconRegion: CLBeaconRegion) { locationManager.startMonitoring(for: beaconRegion) locationManager.startRangingBeacons(in: beaconRegion) }
In this method we call startMonitoring and startRangingBeacons. This allows us to both monitor for iBeacons and range them when they come in to range. We pass in the beaconRegion created by the function above.
We tie this together by adding the following line at the bottom of viewDidLoad:
startScanningForBeaconRegion(beaconRegion: getBeaconRegion())
Here we call the startScanningForBeaconRegion function we just created and pass in getBeaconRegion() which returns the required beaconRegion needed.
You are now ready to test the app. Please ensure that if you are using a physical iBeacon (or some other software iBeacon) that the trackViewController class is set to scan for the correct proximity UUID. If you are using this app on two iOS 10 devices then install on both, hit Track iBeacon on one device and Transmit iBeacon (followed by Transmit) on the other device. When transmit is tapped, you should see the beacon detected on the other device. The closer you move the devices together the proximity and RSSI will increase. Likewise, the further away they are from each other the lower the RSSI value until it gets to a point where it drops out of range.
You can download the full project here.
TransmitViewController.swift
import UIKit import CoreLocation import CoreBluetooth class TransmitViewController: UIViewController, CBPeripheralManagerDelegate { // Labels @IBOutlet weak var uuidLabel: UILabel! @IBOutlet weak var majorLabel: UILabel! @IBOutlet weak var minorLabel: UILabel! @IBOutlet weak var identityLabel: UILabel! var beaconRegion : CLBeaconRegion! = nil var beaconPeripheralData : NSDictionary = NSDictionary() var peripheralManager : CBPeripheralManager = CBPeripheralManager() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. initBeaconRegion() setLabels() } @IBAction func transmitButtonTapped(_ sender: UIButton) { beaconPeripheralData = beaconRegion .peripheralData(withMeasuredPower: nil) peripheralManager = CBPeripheralManager.init(delegate: self, queue: nil) } func initBeaconRegion() { beaconRegion = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "E06F95E4-FCFC-42C6-B4F8-F6BAE87EA1A0")!, major: 1233, minor: 45, identifier: "com.devfright.myRegion") } func setLabels() { uuidLabel.text = beaconRegion.proximityUUID.uuidString majorLabel.text = beaconRegion.major?.stringValue minorLabel.text = beaconRegion.minor?.stringValue identityLabel.text = beaconRegion.identifier } // Delegate Methods func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { if (peripheral.state == .poweredOn) { peripheralManager .startAdvertising(beaconPeripheralData as? [String : Any]) print("Powered On") } else { peripheralManager .stopAdvertising() print("Not Powered On, or some other error") } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
TrackViewController.swift
import UIKit import CoreLocation class TrackViewController: UIViewController, CLLocationManagerDelegate { @IBOutlet weak var iBeaconFoundLabel: UILabel! @IBOutlet weak var proximityUUIDLabel: UILabel! @IBOutlet weak var majorLabel: UILabel! @IBOutlet weak var minorLabel: UILabel! @IBOutlet weak var accuracyLabel: UILabel! @IBOutlet weak var distanceLabel: UILabel! @IBOutlet weak var rssiLabel: UILabel! var locationManager : CLLocationManager! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. locationManager = CLLocationManager.init() locationManager.delegate = self locationManager.requestWhenInUseAuthorization() startScanningForBeaconRegion(beaconRegion: getBeaconRegion()) } func getBeaconRegion() -> CLBeaconRegion { let beaconRegion = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "E06F95E4-FCFC-42C6-B4F8-F6BAE87EA1A0")!, identifier: "com.devfright.myRegion") return beaconRegion } func startScanningForBeaconRegion(beaconRegion: CLBeaconRegion) { print(beaconRegion) locationManager.startMonitoring(for: beaconRegion) locationManager.startRangingBeacons(in: beaconRegion) } // Delegate Methods func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { let beacon = beacons.last if beacons.count > 0 { iBeaconFoundLabel.text = "Yes" proximityUUIDLabel.text = beacon?.proximityUUID.uuidString majorLabel.text = beacon?.major.stringValue minorLabel.text = beacon?.minor.stringValue accuracyLabel.text = String(describing: beacon?.accuracy) if beacon?.proximity == CLProximity.unknown { distanceLabel.text = "Unknown Proximity" } else if beacon?.proximity == CLProximity.immediate { distanceLabel.text = "Immediate Proximity" } else if beacon?.proximity == CLProximity.near { distanceLabel.text = "Near Proximity" } else if beacon?.proximity == CLProximity.far { distanceLabel.text = "Far Proximity" } rssiLabel.text = String(describing: beacon?.rssi) } else { iBeaconFoundLabel.text = "No" proximityUUIDLabel.text = "" majorLabel.text = "" minorLabel.text = "" accuracyLabel.text = "" distanceLabel.text = "" rssiLabel.text = "" } print("Ranging") } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
shilpashree says
Hi,in my scenario i want to detect two types of sensors simultaneously like i sensors and kontakt beacon sensors with in a single project…so i want to use two different UUID with two different regions…now my problem with how to get the details for both sensors based on regions seperately with in didRangeBeacons delegate method…can u help me…
Matthew says
When you have a CLBeacon in didRangeBeacons, you can inspect the .proximityUUID property to see which beacon it is.
Remember that the beacon(s) are stored in an array, so instead of extracting the last one like in the example above, you could consider looping through them each time didRangeBeacons is called (every second) and then as you loop through, inspect each proximityUUID and act as needed.
The tricky part is handling the constant 1 second updates on that function. If you only want to do an action once then you’ll need to track the beacons by making a register to indicate that they are in range and it has been actioned… and then wipe it off the register when it goes out of range so that the action can be performed again when it comes back in to range.
Let me know if that helps. If not, let me know where you’re stuck and I’ll see what I can do.