Tuesday, December 30, 2014

How would you scan for nearby BLE devices using Swift

Since the rise of BLE devices like StickNFind, which can be used in many applications such as Indoor Navigation, Finding Stuff and other useful applications, you would need to build an application that use the power of BLE.

The first step in building a mobile application that uses BLE devices is to know how to scan for them. If you checked the Core Bluetooth Framework Reference you can find a list of all classes and protocols you will need to handle every single scenario in the BLE world.

Here we are going to concentrate on 2 classes and 1 protocol
  1. CBCentralManager (class)
  2. CBPeripheral (class)
  3. CBCentralManagerDelegate (protocol)

As mentioned in the reference CBCentralManager objects are used to manage discovered or connected remote peripheral devices, including scanning for, discovering, and connecting to advertising peripherals. While the CBPeripheral class represents remote peripheral devices that your app—by means of a central manager (an instance of CBCentralManager)—has discovered advertising or is currently connected to. And the CBCentralManagerDelegate protocol defines the methods that a delegate of a CBCentralManager object must adopt.


Creating a wrapper class

We will create a wrapper class to wrap all the functionality that deals with BLE, let's call it BLEManager and initially it will be empty.





Creating a CBCentralManager

Now we want to add a central manager and initialize it, but to initialize a central manager you will need a central manager delegate that implements the functions needed by the central manager to perform its tasks


So, what happens here is that

  1. We added a local variable of type CBCentralManager
  2. We added a local variable of type BLEHandler which acts as a delegate (Note: we will create this class in a minute)
  3. We initialize the ble handler
  4. And finally we initialize the central manager


Creating a CBCentralManagerDelegate

Now we want to create a class called BLEHandler and make a our delegate, so for any class to act as as a CBCentralManagerDelegate it should adopts the CBCentralManagerDelegate protocol.


There is only one required function to be implemented in the delegate which is the centralManagerDidUpdateState function.

The code of the function is user-defined, but to know what code to write in this function we should know first when this function is called, its called when the central manager is first created and whenever its state changed.

So we can check the state of the BLE in this function by the following code



The central manager that changed its state is passed in the function parameters, so we can check its state easily

Now we have a complete code for the BLEManager that compiles and runs but doesn't scan or report any nearby devices, so we have to do 2 things


  1. Start scanning somewhere in our code
  2. Catch detected devices somewhere in our code
Try to answer these 2 questions before going down ...



Start Scanning

When I ask my central manager to start scanning for BLE devices, I should be sure 100% that the Bluetooth is ready, working and ON on my devices. It shouldn't be unsupported, unauthorized, unknown, resetting or powered off. Seems intuitive !! YES :)

OUR BLE SHOULD BE POWERED ON

If you rechecked the piece of code that checks the state of the BLE and read the previous paragraph carefully, you would easily answer the first question by the following piece of code




Simply, if the state is powered ON, start scanning, else do not start scanning


Did Discovered Peripheral ?

Central Manager: Scanning ... Scanning ... Scanning ... Scanning ... Device Discovered .. Let's call some function in our delegate that deals with a discovered peripheral


The signature of this function seems complex but it gives you everything you need to deal with the detected device, here I wrote a simple one line of code that prints its name and RSSI (Received Signal Strength Indication)


Run this code

We have already implemented a complete BLE Manager that detected nearby devices, but we haven't called it anywhere yet.

All you have to do now is to create a variable of type BLEManager and call it's initializer, as follows


You write these lines in the viewDidLoad function of your viewer but ensure that the first line (the variable definition) will not be deleted automatically by defining it as a local variable in the view controller class instead of the viewDidLoad function to last for the lifetime of the view controller.


Final notes

  1. You can wrap the functionality however you want, the BLEManager is just a simple way to wrap and manage things easily.
  2. You can start scanning whenever and wherever you want, not necessary with the creation of the BLEManager (in this code) but do not forget to ensure that the BLE state is poweredOn
  3. Ensure the the BLEManager (specifically), the central manager will last for the lifetime of the application and it's not created as a local variable in a function that will end and clear its local variables.


Where to go from here ?

Now you have a sample code that scan for devices, after that you can search for other functionality that the central manager can do and can be implemented in the delegate like connecting do devices and sending/receiving data to/from devices.

This is a small book that talks briefly about BLE 4.0 technology, you can understand the BLE APIs easily if you understand the BLE technology well.

Finally, try to take a look at iBeacon


New to iOS/Swift ?

This course is taught by Kunal Chawla and it is really a great start for iOS development with Swift.


Thanks for reading my blog