Redux Bluetooth Middleware

Alexander Shtromberg
Hello Heart / Tech Org.

--

Hi there! I’m Alex, a technical lead at Hello Heart. I was hired initially to rebuild the entire application using React Native (why? You’ll find it on our next blog post. Spoiler alert — best decision ever). Oh boy, what a ride it was. I’ve decided to share with you and the internet some of the experience we have gained. This blog is not another recommendation of a library subset! I swear and sure you can realize the same functionality or\and middleware for the Redux approach with any library subset! I don’t have doubts that you are using the most suitable library for your needs.

Before we can continue, the article requires some basic knowledge of React, React-Native, and Redux.

Feature overview:

We found that Redux is a powerful tool that we can use as a main data-flow framework for the application. I like GraphQL very much, it is a very strong approach, I haven’t seen a solution that can utilize your API better. Also, I think MobX is interesting with its simplicity, but as a company, we decided that the most straightforward way for us, to support all our needs and features, is Redux and its ecosystem. In our implementation, data is transiting between API, UI, selectors, reducer, and persistent storage using Redux features and its store.

If you are looking for answers to more basic questions, or you need a particular explanation of what Bluetooth or Middleware is — these two following articles can be useful, also inspired me to write my as well.

Why and how to use Middleware: Middleware explanations and simple examples BleMiddleware

For beginners with react-native or Bluetooth and/or have to support only 1 device, I would recommend you start from them;

Motivation and design:

Bluetooth Low Energy(BLE) is a popular wireless protocol that is applicable in different fields. Here, at HelloHeart, we use BLE to connect to wireless health monitors like Blood Pressure, Glucose Meter, Digital Scales, personal EKG and synchronize them with our application. The application encrypts data and sends it to our backend. Backend learns AI algorithms with all data we send. Relies on users data and our study, using machine learning, we create personal digital coaching that helps our users track, understand, and improve their needs;

We chose react-native-ble-plx as a bridge to work with native Bluetooth functionality. We found it well documented, with good code examples, and supports both major platforms(iOS and Android) that we are supporting currently. All basic and usually BLE functionalities are wrapped with JS layers, so on both platforms, we get similar endpoints to processes that act similarly on different platforms. What is specific for platforms, eg: request Bluetooth permissions, or control Bluetooth Module Power — are different according to platform abilities.

Here is the point, the wisdom we are trying to pass on: Typically the fewer references to the feature(BLE manager in the blog, but actually any feature) you have in the app — the easier is to control its state;

The nature of the feature is much more challenging than it may seem at first. If your app has multiple devices support, complex ways of handling them, like some bonding and some pairing devices, or different UI flow for each — should think about many of possible cases(eg several devices at the same time, loading Bluetooth Native Layer with messages that the app should convert from bytes to mostly strings) to provide your user a smooth user experience.

We have tried different approaches on how to manipulate BLE: singleton manager approach with reference to the manager all over the app, or decorator that passes a reference to the manager that is in saga root generator and trying to handle it there. The main problem is a bottleneck —js- bridge, that we need to control; make it safe, easy to manipulate, and enlarge with different devices or behaviors, encapsulated one! Cause as we have passed a reference to all our instances, especially if we grant abilities to users to control some of them(let’s say — call Scan directly, or call to Connect to the device directly) we are not controlling the feature anymore, the user or an occasion does. So the approach with BLE in Middleware seemed the most appropriate to us. It has one creation point, one entry point, all events come in the same place where we easily can control their execution and notify the system, or us, of a new state or issue.

Realization:

Let’s get our hands dirty: for the example app, I was using react-native cli tool to create the application. Then we installed redux, react-redux, react-native-ble-plx — actually, this is all we need to create react-native app using BLE. Let’s create an app that performs a scan, shows the result, and allows one of the devices to connect.

We need to install:

redux react-redux react-native-ble-plx

The app will be with 3 buttons to control the scanning process and a list of currently connected devices.

In App.js I’ve added Provider; Redux store was made with pure function initStore; the method should be called outside React lifecycle, so, let’s say that store and everything inside is independent of the UI part of the App;

We got something like this:

The most straightforward here are the reducer and initStore, let’s quickly have a look.

Now, let’s dive deeper into the middleware feature and realization. Again, you can see that defining of BleMiddlewareManager is happening inside createBleMiddleware; Also, check and wrap all code that can potentially throw an error with try-catch. Don’t lose your data and log errors! There are a number of solutions for that on the market such as Firebase Crashlytics, App Center Diagnostics etc.

And now let’s see step-by-step what is happening:

Redux middleware is a function that does nothing other than forwarding the action on to the next middleware:

const middleware = store => next => action => next(action)

So our goal is to get the store and use it with our manager, to let it use state and dispatch events. To do that we are calling and storing store reference to a manager with

manager.setStore(store);

Inside the method setStore we are doing something that may throw an error, that’s why it is wrapped with try-catch. We want our middleware to work with the application simultaneously, and clear it with the application as well;

All actions that middleware gets will be passed to the manager with:

manager.handleAction(action);

And the manager will decide what reaction it has related to the action and current store state. We may have (and highly recommend to) another middleware that can use the same action, so we are not filtering our events from the flow, and are calling to

return next(action);

at the end of the handler;

Let’s have a look at the least node of our middleware - BleMiddlewareManager.

handleAction function of the manager works like the reducer handler does, so the whole functionality should look and be familiar to the developer; Realization with batch and debounce-accumulator is experimental.

Epilogue

This is the solution we tried. Unfortunately, there is no Right solution, but there are comfortable to work with solutions, also bad ones that we experienced ourselves. In the next article, we will talk about supporting multiple BLE devices with different ways of pairing and reading from them. I hope you liked this approach and you found it interesting.

Everything that we’ve tried in the article you can find in my repo:

https://github.com/OneStromberg/ble-rn-middleware

Join Us!

If you think this is cool, join us! Visit our careers page to see available mobile engineering roles, or join other roles at the company

--

--