Reactive Heart Rate Monitors in Swift

Leveraging the power of RxSwift and RxBluetoothKit to interact with HR monitors in a seamless way

Leandro Pérez
6 min readFeb 21, 2018
Heartbeat Animation by Alexander Kunze

Integrating Core Bluetooth to connect to Heart Rate Monitors needs a lot of work. If you are familiar with reactive programming, RxBluetoothKit makes things a lot easier. But you still need to understand how Core Bluetooth works and write a considerable amount of code to talk to HR monitors.

RxHeartRateMonitors is a layer on top of RxBluetoothKit and Core Bluetooth. I built it to interact with Heart Rate Monitors in a seamless, reactive way.

You can download the project and try it out.

What does it do?

  • List Heart Rate Monitors only.
  • Connect and disconnect monitors.
  • Read Heart Rate from a monitor.
  • Remembers previously connected monitors.
  • Auto-Connect to previously connected monitors.
  • Read bluetooth state.
  • Read monitors state.

In this article I will explain some details about this project. Why I created it and some implementation details.

Motivation

I am working as part of a team to build a fitness app. One of the requirements the app has is to list Heart Rate Monitors available at reach. Also, users should be able to connect and read values from such devices.

I thought that CoreBluetooth was going to make this an easy task. It was not the case.

Core Bluetooth is too generic

It abstracts the BTLE protocol stack providing means to interact with all kinds of devices.

Discovering peripherals is quite easy. But then you need to discover its services, the characteristics and codes too. Finally, you need to read and convert raw data and into a proper format.

The code can become quite complex. To give you an idea, these are the steps needed to read the heart rate values from a monitor:

  1. Startup a Central Manager.
  2. Discover Peripherals (needs a delegate)
  3. Filter peripherals that provide Heart Rate Monitoring services
  4. Connect to a Peripheral (needs a delegate)
  5. Discover the services for that Peripheral (needs a delegate)
  6. Discover the Characteristics of a Service (needs a delegate)
  7. Find the characteristic that represents the Heart Rate
  8. Subscribing to the Characteristic’s Value (needs a delegate)
  9. Convert the raw data to the proper format.

Too much code is necessary to implement the necessary delegates. You can get a gist of the complexity if you take a look at this tutorial, or this article. Apple has an example too (in Objective-C).

The complexity is high because Core Bluetooth is generic. It was built so you can connect to any kind of BTLE device, not only HR monitors.

RxBluetoothKit helps a lot!

RxBluetoothKit is a really good wrap around Core Bluetooth. It implements all the delegation functionality and provides a reactive interface to the framework. Instead of writing all those delegates you write declarative reactive code.

To me that’s a great improvement. It makes things a lot simpler. For example, scanning devices and obtaining a characteristic from the first one:

There is less code but there are still things to figure out. You still need to read services, characteristics and format the data.

I wanted to connect to a monitor and observe the heart rate.

The problem is quite simple. But even using RxBluetoothKit, the solution is not that simple. While I was working with this, I realized there was a gap between RxBluetoothKit and what I needed.

The code should be simpler. That’s why I created RxHeartRateMonitors.

RxHeartRateMonitors

Again, the idea is top interact with heart rate monitors only, in a seamless way, writing less code.

List HR Monitors only.

I want to see HR monitors, not other kind of devices like thermostats or speedometers. I would like to havelet heartRateMonitors : Observable<HeartRateMonitor>. So I can do things like displaying them on a table:

Heart Rate Monitors. Name and state listed in a UITableView

Connect/Disconnect

Connecting and disconnecting a monitor should be easy. Calling connect returns an observable that will stream the monitor with its new state.

Connecting and disconnecting to a Heart Rate Monitor.

See how I am wiring the tap event of a button, and handling possible errors with materialize() I really like this article by Adam Borek about handling errors, in case you want to know more about materialize()

Read the Heart Rate.

The beats per minute is what users actually need to see. Heart Rate Monitors will publish this value in a stream of unsigned integers:

let heartRate : Observable<UInt> = self.heartRateMonitor.heartRate.

Heart Rate can be displayed in a label like this:

The Heart Rate bound to a label.

Automatically connect to a previously connected monitor.

With the code above, users can see a list of available monitors and connect to them. They shouldn’t have to connect manually each time they strap to a monitor.

For instance, a user might register a heart rate monitor band and in the next day, open the app and start a workout wearing the same band.

With that in mind, it was necessary to automatically connect to a monitor and display the heart rate, without the user having to do anything. I wanted to write something like this:

The library remembers the devices that were connected to the app to later be able to auto-connect to them.

So, RxHeartRateMonitors can list, connect, disconnect, display state, heart rate and auto-connect to saved devices. The resulting code is quite straightforward.

I have explained the requirements and pretty much how to use the library too. In the github repo there is an example that covers the usual cases.

Implementation Details and Extensibility

There are some implementation details that perhaps are worth mentioning in this article. They are useful if you ever need to extend the code to program another kind of central.

If you need to talk to speedometers for example, you could write your own central based on the following explanations.

Important: You don’t actually need to understand or write any of the following code to use RxHeartRateMonitors

Reading data.

Bluetooth devices publish data in raw bits. If you want to get strings, or numbers or whatever, you need to convert those bits properly. I wanted to get unsigned integers that represent the beats per minute of a monitor.

This is how code migrated from Apple’s example to swift:

Code is used to read the heart rate from monitors.

Services and Characteristics Codes

Bluetooth devices support different kinds of services. Each service has a code. The same goes for characteristics. Heart rate monitors publish the heart rate monitoring service and the heart rate measurement characteristic.

This is another thing you need to research and code if you were to use CoreBluetooth or RxBluetoothKit directly. You can see the codes for services and characteristics in the GATT specifications.

This information is used by the central to filter heart rate monitors from other types of devices. Also, RxBluetoothKit uses it to allow developers to write simpler code in Convenience calling methods.

This information is used by the central to filter heart rate monitors from other types of devices.

Generic Central

In the project you will find a BluetoothCentral that talks to RxBluetoothKit and CoreBluetooth. It contains some boilerplate code and does the hard work that any kind of central needs to perform.

There is a generic protocol, SpecifiedBluetoothCentral, that will be implemented by any concrete kind of central. In my case, HeartRateMonitorsCentral. This allows for later extensions to other kinds of devices.

Extending the central

The layer can be extended to create another kind of central, to talk to Thermostats for example. In order to do so, you will need:

  • Create protocols for the device and central. For example, ThermostatMonitor and ThermostatMonitorsCentral.
  • Provide the Services and Characteristics codes.
  • Convert the data to the desire format.

If you follow the code style of the HeartRateMonitor and HeartRateMonitorCentral the implementation should be fairly easy.

Final words

The project is very young and it might contain some bugs. I still need to code a lot of unit tests and add them to the pod.

Please let me know if you have any questions or find any bug or weird things.

Communicating with heart rate monitors should be quite simple, that’s why I wrote this layer. It was written on top of RxBluetoothKit. They did an amazing job at providing a reactive layer on top of CoreBluetooth.

Reactive declarative code helps a lot with asynchronous situations like interacting with BTLE devices. You can read this other article I wrote about this topic.

Did you enjoy this article? Please feel free to leave comments. I would love some feedback!

--

--

Leandro Pérez

Software Engineer, coding since 2001. I have done plenty of work in iOS with Objective-C and Swift.