iBeacon and how to use it in SwiftUI

handyyy
7 min readJun 1, 2023

--

Introduction

iBeacon is a communication protocol introduced by Apple Inc. in 2013, designed to facilitate location-based services and interactions. iBeacons are small, low-power devices equipped with Bluetooth radio transmitters that emit unique signals or identifiers that knows as Bluetooth Low Energy (BLE). These signals can be detected by nearby smartphones or tablets, enabling applications to respond to specific locations or proximity events. An iOS app can listen to signals that generated by iBeacon and triggers the relevant action when the phone or device enters its range. As an example, iBeacon used in Heathrow Airport by Virgin Atlantic to show automatically digital boarding pass in the passenger smartphone when the passenger headed to the checkpoint area.

In this case, I also create an application called FindMyVehicle that use beacon to find the vehicle in parking area. The main idea of this app is because I have a problem where I usually forgot where I parked my vehicle in parking area, so I create this application to help me and help other people that have the same problems, here’s the interface of FindMyVehicle.

Find My Vehicle Interface

In this app, I design it as simple as I can and maximize the main features, which is iBeacon. I also add the CoreData to save the added Beacon identifier in my application. When open this application, user needs to register their Beacon using UUID, Major, and Minor value inside the ‘+’ button, then user need to put beacon in their vehicle. When user want to search their vehicle location, user just open the application and click the registered beacon in the main page.

Let’s back to the main topic, when we talk about iBeacon, you might see the words Beacon. So what the different between iBeacon and Beacon? Both of them have the same BLE technology inside, but iBeacon leverages Apple’s proprietary technology for transmitting signals, so the iOS device like iPad or iPhone are allowed to play as a transmitter and receiver. To use iBeacon, we need the following information from Beacon, such as:

  • UUID, UUID is a standard that generates and assigns unique numbers to a device like iBeacon. This UUID is assigned when you order an iBeacon.
  • Major and Minor, this is set to the iBeacon to improve accuracy with unsigned integer value between 0 and 65535. In this part, Major used to recognize and separate a group. While Minor used to recognize and separate an individual.

Before we start into the code in SwiftUI, we need to know some information if when we want to use detect beacon:

  • CoreLocation Framework, CoreLocation used the region that defined by a geographic location (longitude and latitude) and also the radius that known as Geofence. iBeacon use this region to defining level of flexibility with an identifier.
  • Privacy and Location, because we use the CoreLocation Framework, we need to request user authorization to use their current location in our app.
  • Accuracy, since iBeacon use the Bluetooth Low Energy (BLE) we need to consider the accuracy between iBeacon with the application, because to determine the accuracy, the application (iOS device) detects the strength signal of iBeacon by RSSI (Received Signal Strength Indication) to determine both proximity to the beacon and the accuracy estimation of proximity. The stronger RSSI, the more confidence iOS device about the proximity to beacon and vice versa.
  • Ranging Levels, CoreLocation framework also determining the proximity of a iBeacon that known as Ranging. This framework applies filters to the accuracy estimate to determine an estimated proximity to a beacon. Here’s the following four proximity states:
Proximity State by Apple Developer

So, what are you waiting for? Let’s dive into the code!!

Steps 1: Create the Xcode Project and choose App, then click Next

Create Project in Xcode

Steps 2: Fill the Product Name as you wish and choose SwiftUI as our interface, then click Next.

Create Project in Xcode

Steps 3: Create a IBeaconDetector.swift file to detect iBeacon

import CoreLocation
import SwiftUI

class IBeaconDetector: NSObject, CLLocationManagerDelegate, ObservableObject {
private var locationManager = CLLocationManager()

//MARK: IBeacon Attribute
@Published var range = CLProximity.unknown
@Published var accuracy: CLLocationAccuracy = 0.0
@Published var isStartBeacon: Bool = false

override init() {
super.init()

locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
}

Here’s what we did above:

  • We defined a CLLocationManagerDelegate because this protocol allowing you to receive information about the user’s location, monitor significant location changes, and handle errors related to location services. Because we use this protocol and this protocol is from Objective-C, so we must add NSObject protocol to access it. Defining ObservableObject is to create objects that can be observed and notify views of any changes in their data using ‘@Published’ statement.
  • Inside override init() we request the authorization from user to access their location.

Steps 4: Add DidChangeAuthorization function to check access that granted by user. If user give When In Use authorization and the LocationManager can detect CLBeacon Region also the range is available, then start monitoring the range of Beacon if isStartBeacon is true.

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if manager.authorizationStatus == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) && CLLocationManager.isRangingAvailable() {
isStartBeacon ? startIBeacon() : stopIBeacon()
}
}
}

Steps 5: Add the start and stop ranging the Beacon by adding UUID, Major, and Minor value.

func startIBeacon() {
let uuid = UUID(uuidString: "C9616ADC-E4F3-4DC5-892E-86174E0CB0E6")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 222, minor: 156)
let beaconRange = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Some Identifier")

locationManager.startMonitoring(for: beaconRange)
locationManager.startRangingBeacons(satisfying: constraint)
}

func stopIBeacon() {
let uuid = UUID(uuidString: "C9616ADC-E4F3-4DC5-892E-86174E0CB0E6")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 222, minor: 156)
let beaconRange = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Some Identifier")

locationManager.stopMonitoring(for: beaconRange)
locationManager.stopRangingBeacons(satisfying: constraint)
}

Steps 6: Update the accuracy and ranging level if device is in range

func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
//if beacon is in range, get the ranging levels and accuracy
if let beacon = beacons.first {
self.lastDistance = beacon.proximity
self.accuracy = beacon.accuracy
}
else {
self.lastDistance = .unknown
self.accuracy = 0.0
}
}

Here’s the full code of IBeaconDetector.swift

import CoreLocation
import SwiftUI

class IBeaconDetector: NSObject, CLLocationManagerDelegate, ObservableObject {
private var locationManager = CLLocationManager()

//MARK: IBeacon Attribute
@Published var lastDistance = CLProximity.unknown
@Published var accuracy: CLLocationAccuracy = 0.0
@Published var isStartBeacon: Bool = false

override init() {
super.init()

locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if manager.authorizationStatus == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) && CLLocationManager.isRangingAvailable() {
isStartBeacon ? startIBeacon() : stopIBeacon()
}
}
}

func startIBeacon() {
let uuid = UUID(uuidString: "C9616ADC-E4F3-4DC5-892E-86174E0CB0E6")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 222, minor: 156)
let beaconRange = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Some Identifier")

locationManager.startMonitoring(for: beaconRange)
locationManager.startRangingBeacons(satisfying: constraint)
}

func stopIBeacon() {
let uuid = UUID(uuidString: "C9616ADC-E4F3-4DC5-892E-86174E0CB0E6")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 222, minor: 156)
let beaconRange = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Some Identifier")

locationManager.stopMonitoring(for: beaconRange)
locationManager.stopRangingBeacons(satisfying: constraint)
}

func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
//if beacon is in range, get the ranging levels and accuracy
if let beacon = beacons.first {
self.lastDistance = beacon.proximity
self.accuracy = beacon.accuracy
}
else {
self.lastDistance = .unknown
self.accuracy = 0.0
}
}
}

Steps 7: In ContentView.swift, add this code


import SwiftUI

struct ContentView: View {
@StateObject var detector = IBeaconDetector()

var body: some View {
VStack {
//MARK: Showing range levels
switch detector.lastDistance {
case .immediate:
Text("Immediate")
.font(.largeTitle)
case .near:
Text("Near")
.font(.largeTitle)
case .far:
Text("Far")
.font(.largeTitle)
case .unknown:
Text("Unknown")
.font(.largeTitle)
@unknown default:
Text("Unknown")
.font(.largeTitle)
}


//MARK: Showing Accuracy in metres
Text(String(format: "Accuracy: %0.2fm", detector.accuracy))
.font(.largeTitle)
.padding(.top, 40)


//MARK: Start and Stop ranging
Button(detector.isStartBeacon ? "Stop Ranging" : "Start Ranging") {
//toggle the isStartBeacon
detector.isStartBeacon.toggle()

//if isStartBeacon is false, reset the lastdistance and accuracy
if detector.isStartBeacon == false {
detector.lastDistance = .unknown
detector.accuracy = 0.0
}

//recheck the authorization to start or stop ranging
detector.locationManagerDidChangeAuthorization(detector.locationManager)
}
.buttonStyle(.borderedProminent)
.padding(.top, 80)

}
}
}

Here’s what we did above:

  • We add ‘@StateObject’ variable to indicate that the IBeaconDetector object state may change over time and that any changes to its properties will trigger the appropriate view updates.
  • Showing range level by using switch and showing accuracy from detector.
  • We are adding a button in which the label is assigned by isBeaconStart. Inside it, we toggle the isStartBeacon. Also, reset the range level and accuracy if the isBeaconStart is false. Besides that, we also recheck the authorization to update start and stop beacons.

Steps 8: Before we run this in iPhone, we need to request the privacy to use their location. go to ConnectiBeacon > in Targets click ConnectiBeacon > Info

Configure Location Privacy

Steps 8: Add the privacy for Location When In Use Usage Description and Location Always Usage Description

Configure Location Privacy

Steps 9: Run in your iPhone

Screenshot when simulate in iPhone

Lot of things can be done using this technology, If you want to know more about how I implement iBeacon in FindMyVehicle application, you can download my full source code in the link below.

Conclusion

I feel happy that I can explore about the use case of this technology and connect Beacon using iBeacon Framework in my project. If you feel this article is helpful, feel free to share it to your friend and leave the comment below.

Thanks for reading!

--

--