I have been experimenting with the Bangle.js since getting one and the first question that popped to mind was wether or not it could run react. Turns out, it doesn’t seem likely. 64kb of ram! However, that doesn’t stop a hacker and hackers are who own these right?
I’ve managed to figure out an interesting way to use react with Bangle.js. This post will assume you have used react (or preferably) react-native in some capacity.
Here is a short demo of what it looks like: https://twitter.com/ericlewis/status/1203556182973198336
Before we go much further, the code for this isn’t quite ready for public consumption yet. But I will describe how it works, what I did, and what’s next with possibly other things sprinkled in. Once I get things cleaned up and streamlined (read: easier to use) I will publish all of the code necessary for what is described. Let’s begin!
It’s always important to start with why and there is a why beyond intellectual curiosity in this case.
Upon receiving the bangle it and reading the reference materials I came to the sullen conclusion that to create anything complicated would require a lot of thought and planning about pixels. Less than ideal.
As many Web developers are aware, we don’t really need to do that. We don’t like doing that. We have flexbox.
So the first why is: I wanted to make UI for bangle without worrying about pixels.
Of course, react doesn’t have flexbox built in to it, that’s the DOMs job. And since we have used react for web before, we know that there is a library called react-dom and it gets used to render our app. React is an expressive way to think about and reconcile trees of data. React has a thing called react-reconciler which does exactly what you might suspect. We could think of ReactDOM as a react-reconciler!
But what does that mean for us? Well, it means we can write our own reconciler. Various projects exist which do exactly this. They are often thought of as custom react renderers. React-native does this in the context of native mobile apps. Ink does this for using react to create CLI apps. We are going to do this in order to create a program which can run on the bangle. That’s right. We are going to create a custom react-reconciler implementation which can generate commands like g.drawString()!
Things haven’t gotten too weird yet. It’s reasonable to want to create a static generator using react. What’s weird is flexbox, and how on earth do you do that when we don’t have a DOM to lean on?
Okay wow. That was a lot of stuff. Let’s where we are at now: we have created a way to use react to generate Espruino graphics commands that render a static view on the watch. Nice.
Aside here: you’re probably wondering how we are writing react code without you know, DOM elements. What are our components? And the answer is: there are 2 primitive currently, View & Text. They do what you imagine they would, and map to div & span roughly. This is a similar concept to react native, where there is a handful of primitive components which correspond to underlying native views. In our case. They correspond to draw commands and are used by Yoga for layout. More components could be added to support other graphics features like polygons, image, etc.
Static views are woefully boring, though. And we aren’t using much react since state can’t really change. This next part is where things get kind of complicated, mixed up, really weird, and uncomfortable.
To understand what we are going to do next we will need to take a small journey in to how two things work: the Apple Watch and react-native. Not like... how they work together, but we will get to that...
The original Apple Watch and react-native have a very odd similarity in their functionality: they’re asynchronous by nature. The original Apple watch’s didn’t have the battery in order to run full blown applications on it, so the way apps were written was by creating a host extension, essentially a process which lives on the iPhone that could communicate with the Apple Watch via Wifi or Bluetooth. The views were created statically with storyboards and iOS would run your “watch app” code and transmit the view data to the watch. Watch would send events back to your watch app extension. To be continued..