---
title: Cutting our Blueteeth on React Native
teaser: Leaving the comfort of the React Native ecosystem and embracing the native
  frontier with Bluetooth Low Energy.
tags: react native,javascript,ios,android,mobile
author:
- Ian C. Anderson
- Blake Williams
published_on: 2017-07-17
---

We've been working on a project with [Zagster] that uses [Bluetooth Low Energy
(BLE)][BLE] to connect to a BLE-powered bike lock so that we can authorize,
read, write, and receive notifications from the lock, resulting in a better,
faster, and easier riding experience.

[Zagster]: https://cyclingsoigneur.com/what-happened-to-zagster/
[BLE]: https://www.bluetooth.com/what-is-bluetooth-technology/how-it-works/low-energy

## Round 1: iOS and react-native-ble

For our first attempt we decided to use [`react-native-ble`][react-native-ble]
to connect and
interact with the locks. We were excited to use this library largely because it
would allow us to use the same application code for iOS and Android. After a lot
of hard work and a few PR's we finally got this library talking to the locks,
connecting and generally doing what it is supposed to do.

[react-native-ble]: https://github.com/jacobrosenthal/react-native-ble

## Round 1.5: Android and react-native-ble

After shipping iOS it was time to start focusing on Android. Everything seemed
fine except BLE wouldn't work consistently. We tried changing our JavaScript
code that interacted with the locks, we tried modifying the native code
in `react-native-ble`, we even tried changing phones and locks in case they were
problematic. Nothing we changed seemed to get BLE to work consistently on
Android.

We knew that there were issues with BLE reliability on many Android phones, but
the behavior we were seeing was so erratic that it became clear that our BLE
stack needed an overhaul.

## Round 2: Android, SweetBlue, and react-native-ble

At this point we assumed the issue was the native Java code in
`react-native-ble` so we decided we'd replace it. Instead of re-writing using
the Android included APIs for BLE we decided to use [SweetBlue]. SweetBlue
softens a lot of the sharp edges of the native APIs and provides a much better
interface in comparison to the native APIs.

[SweetBlue]: https://idevicesinc.com/sweetblue/

We eventually got the Java portion of `react-native-ble` replaced with SweetBlue
and things seemed more stable. Unfortunately we still had issues under
non-perfect conditions and real world testing. Things seemed better but we still
weren't able to ship with these issues.

### A 7 Layer BLE(ito)

Debugging the issues we were seeing was overwhelming due to the several layers
of external libraries.

From lowest level to highest level, we had:

- The Java code within `react-native-ble` that uses SweetBlue to interface
  with the Android BLE APIs
- `react-native-ble` bindings, which forwards events from the Java layer to the
  Noble library
- Noble, which provides a generic JavaScript API for interacting with BLE
- Axa.js, a custom library that provides an API to interact with AXA locks
  through Noble, such as authorizing, locking/unlocking, reading the lock
  status, etc.
- Several libraries that use Axa.js to keep track of locks, their status, and
  other metrics/uses.

![7 layer bleito](https://images.thoughtbot.com/bmw-react-native-ble/dcCZtKK0QjmRFIIHYh8P_bleito.png)

Some of these layers were in our app, and some were scattered around GitHub as
their own repositories. Adding logging was not trivial, so the feedback loop was
slow and unreliable. Debugging was also painful. Finding the source of the bugs
and tracking them through the many layers was difficult and not sustainable.

## Round 3: Android and SweetBlue

After trying to massage SweetBlue into `react-native-ble` and experiencing more
issues we only had two options left: inspect/debug each layer extensively, or
remove `react-native-ble` in favor of a custom native module. We opted for the
custom native module and moved all BLE code into the app so we could solve
problems with more agility.

We took the largely generic SweetBlue code we wrote for `react-native-ble` and
developed a native API specific to working with our BLE locks. This allowed us
to put all the complex BLE interactions directly in the Java code removing a lot
of our dependency on JavaScript. This allowed us to reduce the amount of events
flowing between JS and Java, which increased reliability and performance.

After migrating to our custom Java code, connecting and reconnecting to locks
became significantly more stable. We no longer saw the reconnection problems,
failed locking, and other issues that we experienced when using our
`react-native-ble` based stack.

## Round 4: iOS and RZBluetooth

After shipping the Android app, we were left with a somewhat disjointed BLE
stack, since the iOS app was still using `react-native-ble`, while Android was
using our new custom native module. This meant that we had to maintain two very
different paths for handling BLE, which was hard to maintain and reason about.

To consolidate our codebase and to reduce the complexity of BLE on iOS, we
decided to take a similar approach for iOS as we did with Android.

After surveying the BLE library options for iOS, we came across [RZBluetooth],
which is a simple block-based wrapper around iOSs CoreBluetooth. This provides a
more convenient interface for asynchronous operations than working with
delegates and keeps our code consistent with the constructs used in our Java
and JavaScript code.

Migrating was relatively simple. We used the existing Android-specific
JavaScript code to guide the API of the new Objective-C native module. The end
result allowed us to completely consolidate the platform-specific JavaScript
code.

[RZBluetooth]: https://github.com/Raizlabs/RZBluetooth

## LOC (Lines of Code) Impact

Here's a LOC comparison between the approaches we took:

| BLE Stack                           | LOC |
|-------------------------------------|----:|
| `react-native-ble` Java             | 802 |
| `react-native-ble` SweetBlue Java   | 465 |
| `react-native-ble` Objective-C      | 650 |
| Custom SweetBlue Java               | 489 |
| Custom Objective-C                  | 365 |

We removed a lot of code by removing `react-native-ble` and switching to custom
native modules. We were able to remove several dependencies which aren't counted
in the LOC analysis above.

In addition to slimming down the amount of Java and Objective-C code we needed
to maintain we were also able to remove an **entire 759 LOC JavaScript library**
that we wrote to talk between JavaScript and `react-native-ble`. We did have to
write more JavaScript glue code but it was significantly smaller than the axa.js
library we wrote clocking in at only 240 LOC.

## Conclusion

Without going through the pain we experienced, we wouldn't have ended up with
as great a solution. It's easy to look back in hind-sight and think that we
started off on the wrong path, but the knowledge we picked up along the way was
essential to the success of the custom BLE modules.

That being said, if either of us started a new React Native project that depends
on BLE tomorrow, we'd start with custom native modules. We're extremely happy
with the native modules we wrote and both seem to have improved performance and
stability. Most React Native solutions won't require writing native code, but
it's a valuable skill to have when you need more control over low-level
behavior.
