Introduction to BLE with iOS Swift
Exploring iOS native methods for working with BLE
In the ever-evolving mobile technology landscape, Bluetooth Low Energy (BLE) has emerged as a pivotal component, supporting many devices to communicate wirelessly with remarkable efficiency.
Whether you’re developing a fitness tracker, a smart home device, or any IoT gadget, leveraging BLE can significantly enhance your application’s connectivity while conserving battery life.
Before diving into the iOS native methods for working with BLE, let’s review some basic terminology to help you understand the core concepts:
Basic BLE Terminology
Peripheral: Devices that deliver information demanded by the central device. For example, a heart rate monitor is a peripheral.
Central: Devices that seek information from nearby peripherals. In the context of an iOS mobile application connecting to a BLE device, the BLE device acts as a peripheral, and the app serves as the central.
Services: Each device has multiple pieces of information categorized as services, such as a device information service. The central device discovers these services upon successfully connecting to the peripheral.
Characteristics: Data is stored and transferred as characteristics listed under multiple services on BLE devices. Characteristics can be of two main types:
- Notify: The peripheral advertises data packets/information at specific intervals. For notify-type characteristics, the central device does not need to ask for or acknowledge the data; the peripheral sends it automatically. However, the peripheral does not guarantee the delivery of this information to the client.
- Indicate: For indicate-type characteristics, the central device needs to acknowledge the data sent by the peripheral by sending commands back. The peripheral guarantees the delivery of the information to the client.
Getting Started
Firstly, ensure you have a working BLE device to connect and exchange information. This device should be capable of sending advertising data. Here is the basic flow chart showing how BLE works with Swift.
Now, let’s implement BLE functionality in an iOS application using the CoreBluetooth framework in Swift. We’ll cover the steps needed to discover, connect, and interact with BLE peripherals, providing a solid foundation for integrating BLE into your app.
1. Project Setup:
Create a project with basic UI in Xcode and before moving to core Bluetooth methods configure Bluetooth permissions by adding the NSBluetoothAlwaysUsageDescription
key to your Info.plist
file. This key should include a message explaining why your app needs Bluetooth access, which will be displayed when asking the user for permission.
<key>NSBluetoothAlwaysUsageDescription</key>
<string>App needs bluetooth access to connect to nearby devices.</string>
<key>UIApplicationSceneManifest</key>
2. Scan for peripherals (Devices):
To work with Bluetooth import CoreBluetooth and add CBCentralManagerDelegate, and CBPeripheralDelegate to your view controller. Declare a variable of type CBCentralManager and initialize it using the viewDidLoad method. Now, add centralManagerDidUpdateState
to scan the peripheral central with many states, check if it is powered on before scanning.
3. Discover and connect peripherals :
After scanning the peripherals, we will get them in the didDiscover method. We will get all the nearby peripherals, so we will append them to one array of CBPeripherals and use this array in the table view as a data source. We can also filter out the peripherals in the discover method by peripheral name or peripheral identifier. The peripheral identifier is unique for every peripheral.
Now we will configure the table view with this peripheral array data and on didSelectRow we will connect to that device.
4. DidConnect method:
Once you’ve connected to a peripheral in an iOS app, discovering its services is the next step to interact with its peripheral.
In this code, we call discoverServices
on the peripheral. If you pass an array of predefined service UUIDs, it will discover only those specific services. If you pass nil
, it will discover all available services. Additionally, the peripheral's delegate is set to ensure it handles the service discovery callbacks.
5. Discover peripheral services :
Discovering services and characteristics is essential for accessing the peripheral’s data. with the help of didDiscoverServices
method we will be able to discover multiple services and then set a code to discover the respective characteristics of each service.
6. Discover characteristics related to specified service:
After successfully finding services, the next step is to discover characteristics. To retrieve data from characteristics, we must set the notify
property to true. In our demo, we're focusing on reading the battery characteristics.
7. Reading the values:
The peripheral sends information as data packets (unit: Byte), with each bit or byte representing a specific value. For example, in a 1-byte data packet sent by a peripheral/server, the battery level is at index 0, the firmware version is at index 1, and so on. Since we have already set the notify
property to true for the characteristics we are interested in, we will receive data in the didUpdateValueFor
method.
[Note: You need to understand how the data is structured on the firmware side to interpret and process the received data correctly.]
8. Send command to peripheral:
To write data to the peripheral there is a function called writeValue
of peripheral in this we have to pass value, characteristic for which we have to write value and type it can be withResponse
or withoutResponse
it is defined by the peripheral manufacturer for a particular characteristic.
Example Code:
Here is the Demo video of how it works
Bonus Tips:
Filtering Magic: While discovering peripherals, leverage advertisement data for efficient filtering. This saves battery life by focusing on relevant devices.
Leverage UUIDs: Instead of filtering by names (which can be unreliable), use UUIDs (Universally Unique Identifiers) for services and characteristics. These unique codes guarantee you’re interacting with the intended device.
Don’t Forget Timeouts: Set a timer for peripheral discovery to avoid endless scanning. This prevents unnecessary battery drain.
Happy Learning :)
For more updates on the latest tools and technologies, follow the Simform Engineering blog.