How to set up BLE with Swift 2.2
There is a specific sequence that you need to follow for BLE to work with an iPhone. Apple’s documentation is truncated and does not seem to provide a clear flow for these steps. There are snippets online and you can reference other people’s applications but nothing explains what is happening in each step, that I have seen yet. Hopefully this post can help explain it and keep you from running into the same issues I had. As always, there are different ways of implementing so please let me know if there is a more efficient way to handle something.
Step 1: Add CoreBluetooth to your application
First, in order to use Bluetooth you need to add the CoreBluetooth library to your application. To add CoreBluetooth you need to select your parent Xcode file.
In the “Link Binary With Libraries” section click the “+” button, search CoreBluetooth, select and add it.
Now in your ViewController you need to import CoreBluetooth. Along with “import UIKit” add “import CoreBluetooth”.
Step 2: Utilize CBCentral and CBPeripheral from CoreBluetooth
From CoreBluetooth you want to use CBCentralManagerDelegate and CBPeripheralDelegate protocols in your class as this will allow you to use the methods the delegate must adopt, allowing you to manage what acts as the central and the peripheral for the Bluetooth connection. After adding CBCentralManagerDelegate and CBPeripheralDelegate to your “ViewController” class, you’ll want to store the delegate’s objects in a variable so you can call the methods of each. It should look like this so far:
Step 3: Run CBCentralManager
Your phone will act as the central. To start your central manager you can place it in the viewDidLoad() function and delegate it to itself. However, I placed this one in its own function (“startManager”) so that we can control when the phone starts its sequence and I will explain the usefulness of this later.
Step 4: Check that BLE is on and then start scanning
Next, before we can start scanning we need to make sure that bluetooth is powered on for the device. To do so you can use a preset function that is called if the state has been updated and pass in the CBCentralManager object. Once you determine that it is Powered On you can call the function to start scanning (scanForPeripheralsWithServices).
Step 5: Find your BLE device and connect to it
To see all available devices (peripherals) to connect with, you can listen for discovered peripherals with didDiscoverPeripheral. If so, it will return each peripheral object, which you can store. Instead of storing each device I focused on just the one I wanted. Once found, you should save that peripheral to a variable to use it later. To connect to the peripheral you found you can use the method connectPeripheral from centralManager and reference the peripheral. I’ve added in the option to notify me if it disconnects.
Once you connect you will want to stop scanning. It is important to add the centralManager listener didDisconnectPeripheral so you can restart the scanning if it disconnects (this is why I separated the start scan into a separate function).
Step 6: Obtain services, characteristics and send value to device
Once you are connected to the peripheral you can find its services and characteristics. For a peripheral there can be many services and for a service there can be many characteristics. If you are going to write a value or make changes to anything on the BLE device you will want to make sure you are writing to the correct service or characteristic. For what I was trying to do, I needed to write to a specific characteristic of the service that we had on the BLE device. So now instead of using functions from the centralManager object you are using the functions from the peripheral object you saved. To find the services you can call discoverServices once you are connected. Then to see what the services are on the peripheral you have didDiscoverServices listening.
Once discovered, you can call discoverCharacteristics on that service. Having didDiscoverCharacteristicsForService listening will then return with the characteristic object. I cycled through the characteristics to find the one I wanted and from here you can save it in a variable to write to later. Immediately below I have it writing to the characteristic but in the full app have it saved and used later. To write to the peripheral so you can call the method on the peripheral object, writeValue and pass in the encoded value and call out the characteristic that you want to write to.
For what I was looking to do with the application I wanted to roughly know how far I was from the device, which the RSSI reading of the peripheral is good for. Apple has depreciated the RSSI functions so you need to create a workaround. To do so I used a timer (Let me know if you find a better way to handle this). When you find the peripheral it will give you the RSSI once and when you connect to the peripheral it will give it to you again. To keep it streaming you need to keep calling that same function. To read the RSSI you can call the function readRSSI on the peripheral object.
If it read the RSSI then I saved it to a variable to display on my storyboard. The timer then keeps calling the readRSSI function.
You will want to make sure that you stop reading the RSSI when you disconnect from the peripheral.
Below is the full application including reading RSSI and writing once the RSSI hits a certain number