How to work with BLE Devices on iOS

BLE Devices on iOS

BLE (Bluetooth Low Energy Devices) are supported with iPhones, so why not we (developers) explore the possibilities of its implementation. Lets dive deep into BLE device integration with iPhones.

Lets first get familiar with the terminologies of the BLE and learn about Tools of integrating them:

Introduction of the tools

Lets first understand the Central & peripheral devices in bluetooth. The whole BLE communication depends on these two concepts.

Central Device


This device listens to the advertising devices, It sends them command and get their information. You can imagine theses devices as the Reporting Managers which constantly ask for the information and sends command on which reports they need.

Peripheral Devices


This device advertise themselves so they can be found by other devices. These devices has predefined set of commands which they accepts from other devices and they act as data center for providing some information or performing some tasks.

BLE Devices

Lets imagine the Beacon examples. There are malls which has the beacon devices on each sections. Here beacon act as the Peripheral devices which constantly advertise themselves while the iPhone act as the Central device which constantly ask for the informations from the beacons. Here beacons will send them their information using which iPhone can show the latest offer or new collection on that section of the mall.

There are immense possibilities for the application of BLE devices. So let’s not wait and dive into the actual implementation of BLE integration with iPhone:

Setting up the environment

So for the BLE implementation we will need to follow few steps as mentioned below :

1. Create the New xCode Project

Here we will need to create the new xcode project, Make sure you include the unique bundle identifier of your project.

Create the New xCode Project

2. Enable Background Modes to make use of bluetooth even in background.

Well you might be wondering why we need to enable the background modes. The reason is we will need to keep track of bluetooth availability & range even to work in background modes. This is common with most of beacon apps.

Enable Background Modes

So you can enable the background modes via checking the option for “Use Bluetooth LE accessories” tick mark, as described in the above image.

3. Include CoreBluetooth libraries

Now to communicate with iPhone’s bluetooth device we will need to access its Bluetooth Framework, iOS SDK provides CoreBluetooth.framework with set of predefined function & delegate methods for us to communicate with the bluetooth hardware of iOS.

Include CoreBluetooth libraries

We can get the events like change of bluetooth status, new device detection and so on by the delegate methods provided by CoreBluetooth.framework.

Getting Started

Lets dive into the coding part. We will need to follow few steps for the implementation. They are as below:

Step 1: Create the UI

Create UITableView & Label in the Storyboard as shown below :

Create the UI

Step 2 : Setup Central Manager & Initialise the needed variables

We will include the CoreBluetooth File via import statement as described below :

import CoreBluetooth

class ViewController: UIViewController {

@IBOutlet var tableView : UITableView!

@IBOutlet var lblStatus : UILabel!

var centralManager: CBCentralManager?

var peripherals = Array<CBPeripheral>()

override func viewDidLoad() {

super.viewDidLoad()

setupBluetooth()

// Do any additional setup after loading the view, typically from a nib.

}

func setupBluetooth(){

centralManager = CBCentralManager(delegate: self,

queue: DispatchQueue.main)

}

Here we have the setupBluetooth function to initialise the CBCentralManager. We also have the array of CBPeripherals, where we will store all the detected Peripherals in the array.

In the initialisation we tell the CBCentralManager to send the events to our ViewController file by telling delegate : self

Step 3: Setup Delegate Methods for CBCentralManager

extension ViewController : CBCentralManagerDelegate {

func centralManagerDidUpdateState(_ central: CBCentralManager)

{
 if (central.state == .poweredOn){

self.centralManager?.scanForPeripherals(withServices: nil, options: nil)

lblStatus.text = "Available Bluetooth Devices"

}

else {

let alert = UIAlertController.init(title: "Bluetooth Not Switched On", message: "Switch on the bluetooth and try again.", preferredStyle: UIAlertControllerStyle.alert)

alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (UIAlertAction) in

}));

self.present(alert, animated: true, completion: {

})

lblStatus.text = "Bluetooth not swictched on"

}

}

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {

if peripherals.contains(peripheral){

tableView.reloadData()

}

else {

peripherals.append(peripheral)

tableView.reloadData()

}

}

}

It’s always handy to use the extensions so that Delegation code & our business logic remains separate. It’s good practice to have this extensions in our code.

Here centralManagerDidUpdateState method gets called when the bluetooth is switched on or switched off or its state has changed. This method is good for reloading our TableView and ask user to perform some actions.

In Our case we will reload the tableview so that user will have the latest information on their screen and we will prompt user to switch on the bluetooth if they have not switched on the bluetooth. (The reason is we don’t have the access to switch on bluetooth by ourselves so we must prompt user to switch on their bluetooth devices)

Similarly didDiscover method will get called whenever we discover any new peripheral devices which can be beacons, bluetooth speakers and so on. Here we add each peripheral device into the array we have. If you have notices we also check if the peripheral is present in our array list, if it is we don’t add the duplicate. It is always good practice implement these checks.

Now let’s update UI i.e. UITableView & UILabel

Step 4: Implement The TableView Delegates

extension ViewController : UITableViewDataSource {

func numberOfSections(in tableView: UITableView) -> Int {

return 1;

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return peripherals.count

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell

let peripheral = peripherals[indexPath.row]

if peripheral.name == "" || peripheral.name == nil {

cell.textLabel?.text = String(describing: peripheral.identifier)

}

else {

cell.textLabel?.text = peripheral.name

}

return cell

}

}

Here we update the UITableview based on the number of peripheral device we detect. In the cell we either show its name or its identifier.

Step 5: Connect with the Peripheral Device

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {

centralManager?.stopScan()

peripheral.delegate = self

peripheral.discoverServices(nil)

// If you know the UDID of specific service you can pass that here

//peripheral.discoverServices([CBUUID(nsuuid: UUID(uuidString: "")!)])

}

func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {

let alert = UIAlertController(title: "Error", message: "There was error connecting to the device. Try again later", preferredStyle: UIAlertControllerStyle.alert)

alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (UIAlertAction) in

}))

self.present(alert, animated: true, completion: nil)

}

Here on tapping the listed devices you should be able to establish the connection with the device. There are chances that it might fail, so we have both the methods implemented. If you have the specific UUID of the service of the device you can pass it so it would be discovering the specific services.

Step 6: Discover Services & Characteristics

// Extenstion to manage the Peripheral Services

extension ViewController : CBPeripheralDelegate

{

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {

//
 if (error == nil) {

for service in peripheral.services! {

peripheral.discoverCharacteristics(nil, for: service)

}

}

else {

let alert = UIAlertController(title: "Error", message: "There was error discovering Services", preferredStyle: UIAlertControllerStyle.alert)

alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (UIAlertAction) in

}))

self.present(alert, animated: true, completion: nil)

}

}

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

//

if (error == nil) {

print(service.characteristics ?? "")

}
 else {

let alert = UIAlertController(title: "Error", message: "There was error discovering characteristics", preferredStyle: UIAlertControllerStyle.alert)

alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (UIAlertAction) in

}))

self.present(alert, animated: true, completion: nil)

}

}

}

We have the CBPeripheralDelegate delegate available, so we can implement it by extension. It has method “didDiscoverServices” which we will use it for getting all the services for the device.

Once we discover the device we can get its characteristics by implementing “didDiscoverCharacteristicsFor” method. We will use print() to print all the characteristics of the services.

Step 7: Run the App

Press Command + R. Yes you read it correctly. And we are ready with the app which will detect the peripheral devices, will list out its services & print their characteristics.


Conclusion

To implement the BLE device we just need to understand the concept of CentralManager & Peripheral Devices, Explore the CoreBluetooth.framework and play through its delegate methods and have fun.

Downloaded source code

We have the source code ready for you which you can download and simply run to view the demo.

Other Notes

  • You would not be able to run this demo in simulator unless and until you have the external bluetooth device and you know how to configure it on your mac.
  • You will need the BLE device or the device which can act as the Peripheral. If you would try turning on the bluetooth of your phone it will not work as it’s not broadcasting the BLE signals.
  • Debugging: If you need to debug, have the android device with you, install the app : https://play.google.com/store/apps/details?id=net.alea.beaconsimulator. Enable the bluetooth and create iBeacon and advertise the beacon you will see the demo in action.
I am full stack developer, my core expertise is in iOS with 3+ years of experience. But I also have sound knowledge in PHP, WordPress, CodeIgniter, OpenCart, Phonegap & React Native. I love to explore new technologies & drive solutions with my hands on expertise.