By Andrew Jaffee, translator: Grey S; Proofreading: Cee, Numbbbbb; Finalized: Forelax
As iOS developers, we know very well that people love interoperability. It’s obvious that we like to communicate with each other through wireless devices. More recently, we’ve come to expect to be able to communicate with ordinary devices that were once thought of as stand-alone devices. We’ve come to like, even expect, that some wireless devices can collect and analyze their own data (often referred to as “wearables”). Many devices have become such a part of our lives that there is even a term to describe them: “Internet of Things” or “IoT”. There are now billions of wireless devices on the planet. In this tutorial, we will focus on one part of IoT: Bluetooth.
I will explain the basic concepts behind Bluetooth technology and:
- Show you how to master bluetooth-oriented software development that will provide you with great career opportunities
- Note that you have to check if you need to be “qualified” to publish an application using Bluetooth technology.
- To give you an overview of Apple’s Core Bluetooth framework (also see here)
- Finally, take you through Swift 4Core BluetoothAnd a Bluetooth device to develop an iOS app that monitors heart rate
Tip: follow the hyperlinks included in the reading article. This is important information for developers to make sure you fully understand how Bluetooth works and how Apple supports the technology.
Bluetooth – a rapidly evolving technology
It’s impossible to say in a single article how to develop software for the entire Internet of Things, but actually doing data analysis on all of these wireless devices is quite illuminating — actually quite incredible. The connections are everywhere and the growth rate of this little thing is predicted to be phenomenal. If you look at what we’re talking about today, using technologies like Bluetooth and Wi-Fi in the “short range” category, and then adding technologies like telephony in the “wide area category” (i.e. CDMA), you’ll see 12.5 billion devices in 2014 grow rapidly to the projected 30 billion devices in 2022.
Bluetooth is a standard specification of short distance wireless communication technology. The Bluetooth Special Interest Group manages and protects the r&d, development, and intellectual property behind this short-range wireless technology. SIG ensures that manufacturers, developers and sellers of Bluetooth that their hardware and software are based on standardized specifications.
According to Bluetooth SIG, “Nearly 4 billion devices are connected using Bluetooth this year. Bluetooth will connect phones, tablets, PCS, Bluetooth will connect us to each other.” . Ellisys, a firm that invests heavily in short-range communications technology, agrees and “estimates that nearly 4 billion new Bluetooth devices will ship in 2018.” Keep in mind that four billion new Bluetooth devices will hit the market this year alone.
Based on this trend, Statista, a company that collects “market and consumer data,” believes the number of Bluetooth devices worldwide will grow from 3.5 billion in 2012 to an estimated 10 billion by 2018.
What does Bluetooth mean for your career
Dogtown Media LLC, a boutique developer of “Bluetooth apps for the Internet of Things” on iOS, claims that “experts at McKinsey Global Institute predict that within the next nine years, The Internet of Things will have a more than $6 trillion impact on the global economy.” What does this mean for iOS developers like you and me? “The next few years will be exciting, productive, and very profitable for forward-thinking startups and entrepreneurs,” Dogtown said. As a forward-thinking or entrepreneurial young person, you should learn to use Bluetooth for application development, because in this rapidly expanding market, your next task or position will most likely require this skill.
Disclaimer:
- I have no affiliation with Dogtown Media, LLC. While searching for this article, I found the company’s website and saw that they specialize in Bluetooth development for iOS.
- I am a Bluetooth SIG “Adopter” level member.
Before submitting your application developed using Core Bluetooth for approval
In the early days of bluetooth technology, I often saw developers grab a few references and jump right into developing applications involving wireless devices and submitting bluetooth apps to Apple’s AppStore. I want to say: Not so fast, man.
The Bluetooth SIG states that “All products using Bluetooth technology must complete the Bluetooth Qualification Process.” I hear people say, “There are so many Bluetooth-based apps out there; No one will notice me.” Well, not really. Bluetooth technology is copyrighted, patented, and licensed to application developers. If you want your application to be focused and show the fact that you have integrated Bluetooth technology, remember:
Bluetooth trademarks – including Bluetooth text trademarks, graphic trademarks (rune B and oval design), and combination trademarks (Bluetooth text trademarks and design) – are owned by Bluetooth SIG. Only Bluetooth SIG members with corresponding qualifications and declared products can display, function or use any trademarks. To protect these trademarks, Bluetooth SIG manages an enforcement process that monitors the market and audits to ensure that members’ use of trademarks conforms to the Bluetooth brand Guidelines and that the final products released correspond to goods and services that have passed the qualification process.
Bluetooth SIG qualification FAQ
What happens if I don’t apply for the appropriate qualification for my product?
If you fail to apply for the appropriate qualification for your product, you will be the subject of enforcement action. Please read the update strategy here, where we outline the upgrade plan. If no corrective actions are taken, your Bluetooth SIG membership may be suspended or revoked.
Don’t be silly. Don’t take risks. The most important point is that all of us should strive for the highest integrity and honesty, giving credit where it should be given, and promoting compliance with standards so that collaborative work becomes the norm rather than the exception. Thousands of people have contributed thousands of hours of work and millions of dollars to developing bluetooth standards and multiple patents, creating an apparently useful set of intellectual property.
Don’t let me scare you
People are often intimidated by harsh words like “trademark,” “patent,” “copyright,” “qualification,” “membership,” and especially “enforcement.” Don’t start worrying about using Bluetooth for development. Join Bluetooth SIG! It’s free! Click here and then:
Start by becoming an Adopter level member. To develop a product using Bluetooth technology, membership is required. Adopter level members have the following benefits: • A License to manufacture products using Bluetooth technology under the Bluetooth Patent/Copyright License Agreement • A License to manufacture products using Bluetooth Technology under the Bluetooth Trademark License • The ability to network with tens of thousands of Bluetooth SIG members and collaborate in a wide variety of industries — from chip makers to application developers, • Access to tools such as the Profile Tuning Suite (PTS) for protocol and collaborative testing…
Become a member of Bluetooth SIG
Being a member of the SIG has many benefits. You have free access to educational kits, training videos, webinars, developer forums, developer support services, white papers, product testing tools, and help ensure that your application meets international regulatory requirements (primarily regarding RF emissions).
You get some exposure just by becoming a member. My company is a Member of it, so it can be seen in Bluetooth SIG’s Member Directory:
Once you develop an App, get it approved by the SIG and approved by the Apple App Store, your product will also be listed by the SIG and you’ll get more exposure.
It’s easy and cheap to qualify an application
When you are satisfied with your Application based on Core Bluetooth and ready to submit it to the Apple App Store for review, stop and go to Bluetooth SIG’s web page to certify your application. SIG will provide you with a neat “Launch Studio,” an online tool you can use to complete the Bluetooth Qualification Process.”
For most applications, such as the gATT-based Profile Client (APP) I’ll cover in this tutorial, the cost of certification and listing is $100. It will be well worth the effort to make sure your code conforms to the Bluetooth specification and to do some testing. Finally, you can give your application a Bluetooth logo. The trademark is “globally recognizable, with 92 percent consumer awareness.”
Please don’t worry about $100. You’re more likely to land a job with a good salary or hourly wage and handle these Bluetooth compliance issues for the company.
To understand the Core Bluetooth
For the most part, using a Bluetooth device is pretty straightforward. Developing the software to communicate with Bluetooth can be complicated. That’s why Apple created the Core Bluetooth framework:
The Core Bluetooth framework lets your iOS and Mac applications communicate with Bluetooth low-power devices. For example, your application can find, search for, and interact with low-power peripherals, such as heart rate monitors, digital thermostats, and even other iOS devices.
The framework is an abstraction from the Bluetooth 4.0 specification for using low-power devices. That is, it hides many of the low-level details of the specification for you, the developer, making it easier for you to develop applications that interact with low-power devices. Because the framework is based on standard specifications, many concepts and terms from all specifications are adopted…
Note the “low energy device.” When using Core Bluetooth we are not dealing with classic Bluetooth devices such as wireless speakers. Communication with such devices can quickly drain battery power. Core Bluetooth is an API for “Bluetooth Low Energy” (BLE), also known as “Bluetooth 4.0”. BLE uses much less power because it is designed to communicate small amounts of data. A good example of a BLE device is a heart rate monitor (HRM). It sends only a few bytes of data per second. That’s why people can run for an hour with a HRM or with their iPhone and record the change in heart rate during the run without seeing the huge drain on the battery. Note that the number of acronyms like BLE is increasing as this article progresses.
You need to learn a new vocabulary in order for us to be able to discuss Core Bluetooth fluently together.
Consider the BLE protocol from the client/server and producer/consumer models respectively.
The Peripheral device.
Peripheral devices are part of the hardware/software, just like HRM. Most HRM devices collect and/or calculate data such as heart rate per minute, HRM battery level, and so-called “RR-interval”. The device transmits this data to another entity or group of entities that needs it. Peripherals are service providers and producers. Popular HRM in the market are Wahoo TICKR, Polar H7, and Scosche Rhythm+.
I’ll demonstrate the importance of standards like BLE by writing Swift 4 code that connects to all three devices.
The Core perspective of Bluetooth
From Apple’s documentation:
CBPeripheralDelegate
The proxy for the CBPeripheral object must comply with the CBPeripheralDelegate protocol. Agents use this protocol approach to discover, explore, and monitor interactions with services and properties of a remote peripheral. There are no rules in this agreement that must be followed.
The Central
A central device is a piece of hardware/software like iPhone, iPad, MacBook, iMac, etc. These devices can use an app to scan for Bluetooth peripherals like HRM. Central equipment is a customer as well as a consumer. They are connected to HRM, so they can use data like heartbeat per minute, battery level, and “RR-interval” pulled from peripherals. Central device receives these data, can carry out value calculation data, or simply through a user interface display data, or the data is stored for future analysis, display, or aggregation, and data analysis (like statistical analysis need enough data to determine the important and meaningful trend), or other similar operations.
The Core perspective of Bluetooth
From Apple’s documentation:
The CBCentralManagerDelegate protocol defines methods that must be followed by the proxy of the CBCentralManager object. Alternative methods in the protocol allow agents to monitor the discovery, connection, and retrieval of peripherals. The only method that must be implemented indicates the availability of the central device and is called when the state of the central device is updated.
Find peripherals by broadcasting
If your iPhone or iPad can’t find these peripherals and therefore can’t connect to them, then something like HRM is useless. So they’re constantly sending little bits (packets) of data across the wireless spectrum, saying things like: “Hey, I’m a Scosche Rhythm+ heart rate detector; I can provide a function similar to my wearer’s heart rate per minute; I can provide information like my battery level.” When a central device interested in the heart rate finds the peripheral device through a scan, the central device connects to it and it stops broadcasting.
You may have used iPhone -> Settings -> Bluetooth to turn Bluetooth on or off (both traditional and BLE). When toggled on, you can see your iPhone scanning devices and making connections with them, like the two images I cut below, search, and connect my iPhone to a Scosche Rhythm+ HRM:
According to Apple:
Peripheral devices broadcast some data in the form of broadcast packets. A broadcast packet is a relatively small data bundle that may contain useful information provided by the peripheral, such as its name and main function. For example, a digital thermostat might broadcast the current temperature of the room that it can provide. In BLE, broadcasting is the primary way for peripheral devices to demonstrate their presence. A central device, on the other hand, can scan and listen to any peripheral device whose broadcast information is of interest to it…
Later in this tutorial, I will show you how to use Swift 4 to encode peripheral devices to scan and connect to them.
Various services for peripherals
The service may not be what you think it is. A service describes the major features or functions provided by a peripheral. But it is not a specific measurement, such as heartbeats per minute, but rather a category describing heart-related measurements available from peripherals.
According to Apple:
A service is a collection of data and related behavior used to implement a function or feature of a device (or part of a device). For example, one service for a heart rate detector might be to expose heart rate data from the monitor’s heart rate sensor.
To define a Bluetooth “service”, we should look at the Bluetooth SIG “GATT Services” list, where GATT stands for “Generic Attributes”.
Scroll down the list of services until you see “Heart Rate” in the Name column. Note that the Uniform Type Identifier (unified Type Identifier) is the corresponding “org. Bluetooth. Service. Heart_rate”, Assigned Number (code) specified is zero x180d. Note that we will use the value 0x180D in the following code.
Click on “Heart Rate” and you’ll open up a web page with Name in bold: Heart Rate. Please note the Summary, “HEART RATE Service exposes HEART RATE and other HEART RATE sensor-related data for fitness applications.” Scroll down to see that the Heart Rate Service itself doesn’t provide the actual Heart Rate per minute. The service is a collection of other data fragments called Characteristics. Finally, you get one feature that provides important data: heart rate.
The Core perspective of Bluetooth
From Apple’s documentation:
CBService and its subclass, CBMutableService, represent a peripheral service – data and related behavior collected to implement the functionality or features of a device (or part of a device). A CBService object specifically refers to a service for a remote peripheral (represented using a CBPeripheral object). A service group may be primary or secondary, may contain code for a feature group, or may contain a service group (representing other service groups).
Characteristics of peripheral services
Peripheral services are often broken down into more detailed but related information. Features are often where we find important information, real data. Check out Apple’s notes again:
The service itself is composed of features or contained services (in this case, other services). Features provide more detailed service information for peripheral devices. For example, the heart rate service just described might include a feature that describes the target body position of the device’s heart rate sensor and another feature that delivers heart rate measurements.
Let’s continue with HRM as an example. Return to the screen that says Name: Heart Rate in bold type. Scroll down until you see Service Characteristics. It’s a big table with a lot of metadata (data about information). Please find the Heart Rate Measurement (Heart Rate) and click on org. Bluetooth. Characteristic. Heart_rate_measurement then review. I’ll explain this interface later.
Core Bluetooth Perspective from Apple documentation:
CBCharacteristic and its subclass, CBMutableCharacteristic, represent detailed information about peripheral services. CBCharacteristic objects refer specifically to the characteristics of the remote peripheral services represented using CBPeripheral objects. A feature contains a single value and any descriptor that describes the value. The attributes of a feature describe how the value of the feature is used and how to access these descriptors.
GATT specification
When you use Core Bluetooth to develop an application that needs to interact with Bluetooth peripherals, you should first go to the Bluetooth SIG home page.
Let’s go back to a time when I was developing an application that did all sorts of fun things with HRM. Check the GATT Specifications section and find the peripheral Services you need under GATT Services.
In the HRM example presented in this article, first find the “Heart Rate” (that is, a hyperlink) entry in the Name column of the GATT Services interface. Click on the “Heart Rate” link and view the full site. Please remember the Assigned Number (0x180D) and slide Service Characteristics at the bottom. Look carefully at the table and find interesting features.
In this example, read the Heart Rate Measurement and Body Sensor Location sections and click on the detailed links for each, Org. Bluetooth. Characteristic. Heart_rate_measurement and org. Bluetooth. Characteristic. Body_sensor_location.
In the Heart Rate Measurement and Body Sensor Location screens, remember their Assigned Number, (0x2A37) and (0x2A38), respectively, Then look at all the information in the interface to see how to interpret the Bluetooth-encoded data structure that will be sent to the HRM application. Code must be written to convert bluetooth encoded data into a human-readable format.
I will give you more details as this tutorial progresses, especially when I show you the application code I use to communicate with BLE HRM.
If you join Bluetooth SIG, you can get more detailed information about programming with services and features.
Write Core Bluetooth code
In this discussion, I’ll assume that you know the basics of iOS application development, including the Swift programming language and the Xcode Single View App template. Test the application’s USER interface (UI), including Auto Layout, as shown in the code below, which is very simple.
I’ll describe the code in a series of steps – these steps are also explained in the code below. Therefore, as you read through the steps in this section, see the corresponding steps in the code below. The whole process is basically linear. Keep in mind that some of these steps represent callbacks – the delegate method being invoked.
When writing applications, I break Core Bluetooth components into protocols or classes — for example, separating Core functionality from the UI. But the purpose of this code is to show you how Core Bluetooth works with minimal interference. My notes are simple and practical. You only see the important parts of a page.
Sample application style
The application UI I developed for this article is extremely simple. When the application is launched, it starts scanning and tries to match a HRM. The scanning process is indicated by the UIActivityIndicatorView class displayed on the screen and rotated. When no HRM is matched, this is indicated by a red square UIView. Once a HRM is found and the initial link is made, the UIActivityIndicatorView stops spinning and hides, and the red UIView turns green. When HRM is fully linked and accessed, I will display the HRM brand number and the intended location of the wearer’s placement on the body. At this point I will start reading and displaying the wearer’s heart rate per minute, updated approximately every second. Most HRMS send heart rate per minute values once per second. I made an artificial pulse animation of my heart rate number to make the app look more attractive, but what you’re seeing is my real heart rate. When HRM disconnects, I empty all the informational text, turn the square UIView to red, display UIActivityIndicatorView and start spinning, while starting to scan HRM again.
Here is how my application looks when run with HRM from three different brands — Scosche Rhythm+, Wahoo TICKR, and Polar H7:
Rhythm+ Use infrared light to “look” at my veins to determine heart rate. TICKR and H7 use electrodes to detect the electrical pulses that tell me my heartbeat.
Learn my code step by step
You can find the full source code in the next section. Here, I’ll walk you through the implementation steps.
Step 0.00: I have to import the CoreBluetooth framework.
Step 0.0: Assign constant Numbers in GATT. I did this to make the Bluetooth specification identifier more readable and maintainable for the “heart rate” service, its “heart rate measurement” feature, and its “body sensor position” feature.
Step 0.1: create a subclass of UIViewController HeartRateMonitorViewController. Make HeartRateMonitorViewController observe CBPeripheralDelegate CBCentralManagerDelegate * * * * and * * * *. I use protocol and delegation design patterns, as I describe here and here in the AppCoda article. We will implement methods from both protocols. We’ll call some Core Bluetooth methods, and some methods will be called by Core Bluetooth for us in response to our own calls.
Step 0.2: we defined in HeartRateMonitorViewController class instance variables, they represent CBCentralManager and CBPeripheral class, so they are in the application of life cycle are persistent.
Step 1: We create a concurrent queue for the process in the background. I want Core Bluetooth to run in the background. I want the UI to be responsive. Perhaps, in a more complex application, HRM might run for hours collecting heart rate data for the user. The user might want to use other application features, for example, to modify application Settings, or if the user is running and wants to use Core Location to track the route of the run. Thus, users can collect and/or view their geographical location while heart rate data is being collected and displayed.
Step 2: Create a control center for scanning, connecting, managing, and collecting data from peripherals. This is a necessary step. Core Bluetooth will not work without a control center. Another essential: Due to the HeartRateMonitorViewController adopted CBCentralManagerDelegate, We will centralManager entrusted property set to HeartRateMonitorViewController (self). We also specify a DispatchQueue for the control center.
Step 3.1: centralManagerDidUpdateState method calls based on bluetooth state of the equipment. Ideally, we should consider a scenario in which the user inadvertently (or intentionally) turns Bluetooth off in the Settings application. We can only scan peripherals if the Bluetooth is.poweredon.
The control center should scan peripherals of interest, but only if the device (such as an iPhone) has Bluetooth enabled. Remember the section above entitled “Finding peripherals by broadcasting”? That’s how we handle the call. Our listening is only for HRM broadcasting the heart rate service (0x180D). We can listen on and connect more peripherals by adding CBUUIDs for specific services to the serviceUUIDs array parameter (marked withServices). For example, in some health-related applications, we can listen and connect to HRM and blood pressure monitors or BPM (although we need to create another instance variable of the CBPeripheral class). Note that if we make this call:
centralManager? .scanForPeripherals(withServices:nil)
Copy the code
We can listen in on any Bluetooth broadcast within range. It may be useful in some bluetooth-enabled applications.
Step 4.1: Find out what peripherals of interest (HRM) the application can connect to. This didDiscover method tells us that the CONTROL center has found the HRM being broadcast at the time of the scan.
Step 4.2: We must keep the reference to the newly discovered peripheral in the instance variable of the class, and it will persist. If we just use a local variable, we’re in trouble.
Step 4.3: Because HeartRateMonitorViewController adopted CBPeripheralDelegate agreement, So peripheralHeartRateMonitor object must be its delegate property is set to HeartRateMonitorViewController (self).
Step 5: In didDiscover, we told the control center to stop scanning to protect battery life. When the connected HRM or peripherals are disconnected, we can start scanning again.
Step 6: while still in didDiscover, we connect to the found peripheral of interest, a HRM.
Step 7: didConnect method is only “called when successfully connecting to a peripheral.” Note the word “success”. If you find a peripheral but can’t connect, you need to do some debugging. Notice that I updated the UI to show that I connected that peripheral, to show that I had stopped scanning, among other things.
Step 8: While still in the didConnect method, we look for services of interest on the peripherals. Specifically, we want to find the Heart Rate (0x180D) service.
Step 9: When the didDiscoverServices method is called, the “Heart Rate” service is found on the peripheral to which we are connected. Remember that we need to look for features of interest. Here I loop through all the features of the Heart Rate service to find the one I’m going to use next. If you go to the “Heart Rate” Service page on the Bluetooth SIG web page and scroll to the section labeled Service Characteristics below, you can see the three available features.
Step 10: didDiscoverCharacteristicsFor service method prove that we have found all the characteristics in the service of interest.
First, I subscribe to a notification — “Read” — about the Body Sensor Location I’m interested in. Go to the page for the “Heart Rate” service and you will find that this feature is marked as “Read Mandatory.” Call peripheral. ReadValue will cause peripheral: didUpdateValueForCharacteristic: error: method is called, later so I can put this feature into human language. Second, I subscribe to a periodic notification — “Notify” — about the Heart Rate Measurement features of interest. Go to the “Heart Rate” service page and you will find that this feature is marked as “Notify Mandatory.” Call peripheral. SetNotifyValue will cause peripheral: didUpdateValueForCharacteristic: error: The method is called later and fires almost every second, so I can parse this feature into human language.
Step 12: Because I subscribe to read values for the feature Body Sensor Location (0x2A38) and to periodic get notifications for the feature Heart Rate Measurement (0x2A37), So if they send values or update periodically, I’ll get these two binary values separately.
Translate BLE Heart Rate Measurement data into a human-readable format. Go to the GATT specification page to find this feature. The first byte is metadata (markup) about the rest of the data. The specification tells me to look at the least significant bit of the first byte, the Heart Rate Value Format bit. If it is 0 (zero), the number of heartbeats per minute will be in UINT8 format in the second byte. I have never come across a HRM that uses anything other than the second byte, and the three HRMS I demonstrate here are no exception. That’s why I’ve ignored the use case where the Heart Rate Value Format bit is 1 (one). I’ve seen all the implementations mentioned, but have never been able to test them. I’m not going to say anything about a situation that I can’t recreate.
Interpret BLE Body Sensor Location data into a human-readable format. Go to the GATT specification page to find this feature. This feature is very simple. Store the values 1, 2, 3, 4, 5, 6, or 7 in 8 bits. The resulting text string is the same as the presentation of the values for interpretation purposes.
When a peripheral is disconnected from the control center, take appropriate action. I updated my UI and…
Start scanning for a peripheral device that is broadcasting the Heart Rate service (0x180D).
My source code
Here is the complete source code for the implementation we just discussed:
import UIKit
// STEP 0.00: CoreBluetooth Framework must be imported
import CoreBluetooth
// STEP 0.0: Assign constant "Assigned Numbers" in the GATT for better readability and maintainability
// MARK: -core Bluetooth service ID
let BLE_Heart_Rate_Service_CBUUID = CBUUID(string: "0x180D")
// MARK: -core Bluetooth feature ID
let BLE_Heart_Rate_Measurement_Characteristic_CBUUID = CBUUID(string: "0x2A37")
let BLE_Body_Sensor_Location_Characteristic_CBUUID = CBUUID(string: "0x2A38")
// STEP 0.1: This class uses delegate protocols for both the control center and peripherals, so the requirements of these protocols must be followed
class HeartRateMonitorViewController: UIViewController.CBCentralManagerDelegate.CBPeripheralDelegate {
// MARK: -core Bluetooth class member variable
// STEP 0.2: Create instance variables for CBCentralManager and CBPeripheral respectively
// So they persist throughout the life cycle of the application
var centralManager: CBCentralManager?
var peripheralHeartRateMonitor: CBPeripheral?
// MARK: - UI outlets /
@IBOutlet weak var connectingActivityIndicator: UIActivityIndicatorView!
@IBOutlet weak var connectionStatusView: UIView!
@IBOutlet weak var brandNameTextField: UITextField!
@IBOutlet weak var sensorLocationTextField: UITextField!
@IBOutlet weak var beatsPerMinuteLabel: UILabel!
@IBOutlet weak var bluetoothOffLabel: UILabel!
/ / set HealthKit
let healthKitInterface = HealthKitInterface(a)// MARK: - UIViewController delegate
override func viewDidLoad(a) {
super.viewDidLoad()
// After the view is loaded, usually through a NIB, all additional Settings are done.
// Initially, we were scanning and no connection was made
connectingActivityIndicator.backgroundColor = UIColor.white
connectingActivityIndicator.startAnimating()
connectionStatusView.backgroundColor = UIColor.red
brandNameTextField.text = "--"
sensorLocationTextField.text = "--"
beatsPerMinuteLabel.text = "-"
// In case Bluetooth is turned off
bluetoothOffLabel.alpha = 0.0
// STEP 1: Create a concurrent queue in the background for the control center
let centralQueue: DispatchQueue = DispatchQueue(label: "com.iosbrain.centralQueueName", attributes: .concurrent)
STEP 2: Create a control center for scanning, connecting, managing, and collecting data from peripherals.
centralManager = CBCentralManager(delegate: self, queue: centralQueue)
// Read heart rate data from HKHealthStore
// healthKitInterface.readHeartRateData()
// Read the gender type from HKHealthStore
// healthKitInterface.readGenderType()
}
override func didReceiveMemoryWarning(a) {
super.didReceiveMemoryWarning()
// Process any resources that can be recreated
}
// MARK: - CBCentralManagerDelegate methods
// STEP 3.1: This method is called based on the bluetooth state of the device;
// Peripherals can be scanned only if Bluetooth is.poweredon
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("Bluetooth status is UNKNOWN")
bluetoothOffLabel.alpha = 1.0
case .resetting:
print("Bluetooth status is RESETTING")
bluetoothOffLabel.alpha = 1.0
case .unsupported:
print("Bluetooth status is UNSUPPORTED")
bluetoothOffLabel.alpha = 1.0
case .unauthorized:
print("Bluetooth status is UNAUTHORIZED")
bluetoothOffLabel.alpha = 1.0
case .poweredOff:
print("Bluetooth status is POWERED OFF")
bluetoothOffLabel.alpha = 1.0
case .poweredOn:
print("Bluetooth status is POWERED ON")
DispatchQueue.main.async { () -> Void in
self.bluetoothOffLabel.alpha = 0.0
self.connectingActivityIndicator.startAnimating()
}
// STEP 3.2: Scan peripherals we are interested incentralManager? .scanForPeripherals(withServices: [BLE_Heart_Rate_Service_CBUUID])}// END switch
} // END func centralManagerDidUpdateState
// STEP 4.1: Find out which peripherals of interest the application can connect to
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral.name!)
decodePeripheralState(peripheralState: peripheral.state)
// STEP 4.2: a reference to the peripheral must be stored in the instance variable of the class
peripheralHeartRateMonitor = peripheral
/ / STEP 4.3: because HeartRateMonitorViewController adopted CBPeripheralDelegate agreement,
/ / so peripheralHeartRateMonitor must be set to him
/ / the delegate property to HeartRateMonitorViewController (self)peripheralHeartRateMonitor? .delegate =self
// STEP 5: Stop scanning to protect battery life; Scan again when the link is disconnected.centralManager? .stopScan()// STEP 6: Establish connections with discovered peripherals of interestcentralManager? .connect(peripheralHeartRateMonitor!) }// END func centralManager(... didDiscover peripheral
// STEP 7: "Called when a connection to the peripheral has been successfully created."
// Only when we know that the connection to the peripheral has been established can we proceed to the next step
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
DispatchQueue.main.async { () -> Void in
self.brandNameTextField.text = peripheral.name!
self.connectionStatusView.backgroundColor = UIColor.green
self.beatsPerMinuteLabel.text = "-"
self.sensorLocationTextField.text = "--"
self.connectingActivityIndicator.stopAnimating()
}
STEP 8: Look for services of interest on peripheral devicesperipheralHeartRateMonitor? .discoverServices([BLE_Heart_Rate_Service_CBUUID])}// END func centralManager(... didConnect peripheral
When a peripheral disconnects, use the appropriate method
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// print("Disconnected!" )
DispatchQueue.main.async { () -> Void in
self.brandNameTextField.text = "--"
self.connectionStatusView.backgroundColor = UIColor.red
self.beatsPerMinuteLabel.text = "-"
self.sensorLocationTextField.text = "--"
self.connectingActivityIndicator.startAnimating()
}
// STEP 16: In this use case, start scanning the same or other peripherals, as long as they are HRM, to come back onlinecentralManager? .scanForPeripherals(withServices: [BLE_Heart_Rate_Service_CBUUID])}// END func centralManager(... didDisconnectPeripheral peripheral
// MARK: - CBPeripheralDelegate methods
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
if service.uuid == BLE_Heart_Rate_Service_CBUUID {
print("Service: \(service)")
// STEP 9: Look for interesting features in the services you are interested in
peripheral.discoverCharacteristics(nil.for: service)
}
}
} // END func peripheral(... didDiscoverServices
// STEP 10: Identify the features we find interesting from the services we are interested in
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print(characteristic)
if characteristic.uuid == BLE_Body_Sensor_Location_Characteristic_CBUUID {
// STEP 11: Subscribe to a single notification of interesting features;
// "When you use this method to read the value of the feature, the peripheral will call...
/ / peripheral: didUpdateValueForCharacteristic: error:"
//
// Read Mandatory
//
peripheral.readValue(for: characteristic)
}
if characteristic.uuid == BLE_Heart_Rate_Measurement_Characteristic_CBUUID {
// STEP 11: Subscribe to constant notifications of interesting features;
// "When you enable eigenvalue notification, the peripheral calls...
/ / peripheral (_ : didUpdateValueFor: error:)"
//
// Notify Mandatory
//
peripheral.setNotifyValue(true.for: characteristic)
}
} // END for
} // END func peripheral(... didDiscoverCharacteristicsFor service
// STEP 12: We will be notified whenever an eigenvalue is periodically updated or published.
// Read and interpret our subscription eigenvalues
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == BLE_Heart_Rate_Measurement_Characteristic_CBUUID {
Usually we need to parse BLE data into a human-readable format
let heartRate = deriveBeatsPerMinute(using: characteristic)
DispatchQueue.main.async { () -> Void in
UIView.animate(withDuration: 1.0, animations: {
self.beatsPerMinuteLabel.alpha = 1.0
self.beatsPerMinuteLabel.text = String(heartRate)
}, completion: { (true) in
self.beatsPerMinuteLabel.alpha = 0.0})}// END DispatchQueue.main.async...
} // END if characteristic.uuid ==...
if characteristic.uuid == BLE_Body_Sensor_Location_Characteristic_CBUUID {
Usually, we need to parse BLE data into a human-readable format
let sensorLocation = readSensorLocation(using: characteristic)
DispatchQueue.main.async { () -> Void in
self.sensorLocationTextField.text = sensorLocation
}
} // END if characteristic.uuid ==...
} // END func peripheral(... didUpdateValueFor characteristic
// MARK: - Utilities
func deriveBeatsPerMinute(using heartRateMeasurementCharacteristic: CBCharacteristic) -> Int {
let heartRateValue = heartRateMeasurementCharacteristic.value!
// Convert to an unsigned 8-bit integer array
let buffer = [UInt8](heartRateValue)
// UInt8: "an 8-bit unsigned integer type."
// The first byte (8 bits) in the buffer is the token (metadata, which is used to manage the rest of the package);
// If the least significant bit (LSB) is 0, the heart rate (BPM) is in the UInt8 format,
// If LSB is 1, BPM is UInt16
if ((buffer[0] & 0x01) = =0) {
// Second byte: "Heart rate format is set to UINT8"
print("BPM is UInt8")
// Write the heart rate to HKHealthStore
// healthKitInterface.writeHeartRateData(heartRate: Int(buffer[1]))
return Int(buffer[1])}else { // I've never seen this use case, so I'll leave it to theorists to argue
// Second and third bytes: "Heart rate format is set to UINT16"
print("BPM is UInt16")
return -1}}// END func deriveBeatsPerMinute
func readSensorLocation(using sensorLocationCharacteristic: CBCharacteristic) -> String {
let sensorLocationValue = sensorLocationCharacteristic.value!
// Convert to an unsigned 8-bit integer array
let buffer = [UInt8](sensorLocationValue)
var sensorLocation = ""
// Just look at the 8 bits
if buffer[0] = =1
{
sensorLocation = "Chest"
}
else if buffer[0] = =2
{
sensorLocation = "Wrist"
}
else
{
sensorLocation = "N/A"
}
return sensorLocation
} // END func readSensorLocation
func decodePeripheralState(peripheralState: CBPeripheralState) {
switch peripheralState {
case .disconnected:
print("Peripheral state: disconnected")
case .connected:
print("Peripheral state: connected")
case .connecting:
print("Peripheral state: connecting")
case .disconnecting:
print("Peripheral state: disconnecting")}}// END func decodePeripheralState(peripheralState
} // END class HeartRateMonitorViewController
Copy the code
conclusion
I hope you enjoyed this tutorial. Buy or borrow a BLE device and then use my code or write my own code to connect it. Follow all the hyperlinks I’ve provided in the tutorial and read them. Consult the Bluetooth SIG web page and apple’s Core Bluetooth framework documentation (also available here), and you’re sure to get an overview of Bluetooth technology.
Thanks for reading. Remember to enjoy your work. Don’t forget that having Bluetooth experience on your resume can be a highlight of your career.
For reference, you can check out the full source code on GitHub.
This article is translated by SwiftGG translation team and has been authorized to be translated by the authors. Please visit swift.gg for the latest articles.