dec 1, 2016 · 14 min

React Native: Why and How to Build Your Native Code in Go

React Native allows us to build mobile apps, using Javascript. To me it’s the first platform that actually delivers on that promise; it does this through a mindful architecture, best-in-class tooling and workflow, and a pragmatic approach for cross-platform development. It didn’t state its purpose is to provide an ability to write 100% Javascript code for any given project, nor did promise a perfect, single, cross-platform codebase for your mobile apps. To me, that is the entire difference.

And so, the idea of perfect cross-platform code sharing and an app that’s built entirely in Javascript is inherently tricky, because:

  1. Some parts you have to build natively.
  2. Different platforms are different in experience.
  3. Different devices perform differently.
  4. There’s no silver bullet.

This means that ultimately we’re all bound to write some native code. Writing native code is a radical shift from our React Native experience: we have to know the tooling more intimately; Xcode, Android Studio and Gradle and the Android SDK, Obj-C or Swift, and Java or any Android-friendly JVM language such as Kotlin. We also need to get familiar with each platform’s framework mental model, such as Android’s Intents and Views, and Cocoa’s controllers, views and delegates.

Why Go Native?

But before that, even when on React Native, we can list real reasons that force us to ditch Javascript, and go native.

As React Native evolves, some of the items in this list will disappear, and still some, like obfuscation and others, will always remain.

Using Go for Cross-Platform Mobile Development

Ruling out guest VMs that, as Javascript, host a language on a given platform, or commercial engines like Apportable, we’re left with C and C++ (dropbox used C++ for sharing non-UI code) as target languages for native code sharing.

But these are not safe (as in memory safety), and in comparison to Javascript, are hard core. Using Java, Obj-C or C/C++ may create a considerable amount of friction. There’s also Rust, but I’m not sure how well it plays with ARM, and what kind of mobile abstractions it has (none?).

Up until a couple years ago, that could have been where our experiment ended. Then, Go (the language) has added support to export binaries as C-libraries, which opened up a world of opportunities. For example, years ago I used Go to fix performance bottlenecks in Ruby, and later wrote an article and a reference Gem implementation to show how you could do it too.

Another of which was putting Go on mobile devices. I’m going to show you that Go is as good a language to use for cloud infrastructure, as well as mobile development, and — to great extent — mobile development for React Native.

Our Prisma Clone: Primer

We’ll make an app that render images like this:

We use Primitive by Michael Fogleman

If you’re wondering about the image processing algorithm behind this image, it’s called primitive, by Michael Fogleman, and you can also buy a Mac app that he built here.

We’ll take this algorithm and build a mobile app around it, where the algorithm is originally written in Go.

And this is how it will looks like live (video is on fast-speed):

Since the core algorithm is native, and built in Go, we’ll use it as-is. But to be clear, if we had to build this algorithm or any other algorithm or piece of infrastructure that must be native, and we didn’t have an existing codebase already, we had a hard decision to make:

Actually, for this particular use case and algorithm, we tick even more items from the “why go native” list above:

  1. Performance. Go will exhibit better performance than Javascript (we’ll see by how much later). We need it here, since primitive is a demanding cpu-based algorithm. Actually, it’s so demanding that it makes processing these images on a strong iPhone slow. Go is closer to the metal here and makes a good case to use it instead of Javascript.
  2. Using a library that doesn’t exist on npm. And this time, because it’s a novel algorithm implemented in Go, and there’s no port of that any where else, I was happy that Go on mobile (and later, I made it work on React Native) works so well.
  3. Obfuscation. In this case the target Go library we’re using is open source. If it weren’t the case, and you cared about your IP, no one could reverse engineer this algorithm without investing an unreasonable amount of time.
  4. Native API access. This algorithm can save progress snapshots to disk on every iteration. If this were to be Javascript, we’d have to encode each snapshot and send it over as a big base64 string across the React Native bridge.

And of course, we can use the same native code for both Android and iOS.

The Go Mobile Toolchain

If you’re already familiar with Go as a language, then this will be a nice fresh breeze. If not, to get familiar with Go, check out the Go tour.

After you’ve done that, let’s install and exercise the gomobile toolchain (I assume you’ve installed Go and were able to do a simple “hello world” with it in the meanwhile):

$ npm install -g ios-deploy
$ go get golang.org/x/mobile/cmd/gomobile
$ gomobile init # it might take a couple minutes
$ gomobile build -target=ios golang.org/x/mobile/example/basic
$ ios-deploy -b basic.app

If everything worked well, you should be seeing a mobile app in your simulator. What this mobile toolchain did is build a native iOS library, generate a skeleton app, link these, and package an app. It all happened quietly (or humbly?), that’s part of the Go philosophy: success should be silent; in other words — no news is good news.

I encourage you to read the short Wiki article. To get proper context about these tools.

Connecting React Native and Go Mobile

We start by making a fresh React Native app, and grab the react-native-camera example here. This is a convenient way to get to the point of this article but you may want to build your own from scratch.

We want to build our native Go library now and start by connecting it to our iOS app. This is where it might become tricky because we need to think about our workflow before everything else.

In Go, the best thing to do is develop your Go code in your GOPATH, which is an opinionated place where your Go code and third-party libraries live (there’s a solid logic around that, but it’s out of the scope of this article). And so our first decision is to put our native library there as well — and not alongside our app.

Once nice bonus is that our new Go package is ignorant of the fact that we’ll use it on a mobile device. In fact, you can conveniently work on it as a stand-alone Go app and then decide to bind it to your mobile app when you’re ready.

For me, it lives here (yours will be different):

<home>/workspaces/golang/src/github.com/jondot/primer-bind

The layout is simple:

➜  primer-bind tree .
.
└── primer
    └── prime.go

1 directory, 1 file

Let’s take a look at our Go code:

A quick pass shows that we export OnIterationDone which is a callback like interface we’ll use from each native side and Process which is the entry point to our algorithm. There’s nothing special to do about types passing in and out; go takes care of generating code for marshalling these to the respective mobile platforms.

For Process, I took the CLI code from primitive and hacked it so it would be a function I can export. We also export Bench and JsonDummy for our benchmarking showcase.

You’ll see that in each step in this article, I take the smallest possible step to strategically create code that we can discuss without too much context.

Once this is in place, we have to switch context and cd to our app. We now want to make use of the gomobile toolchain, so we’ll run this:

gomobile bind -x -v -target=ios github.com/jondot/primer-bind/primer

It will generate a native library for you and spit a lot of detail while doing so because we’re using -x -v; but you can drop these if you like. You can see the beauty of treating every Go code like a package: we supply a path that is both a repo and a local path in your GOPATH, if you didn’t have a local copy, Go would fetch it and build using it, or leave this as-is and it’ll grab my package and compile it so that you wouldn’t have to.

Next up, we’ll have to build a bridge in our native code to glue everything together.

Here, we’re making an RCTEventEmitter because we want to get notified where the native Go algorithm finishes its work. We also wrap our OnIterationDone struct with a nicer Obj-C interface. For where headers sit, it correlates with our Go package (we chose primer).

Observation: this is how you would build a native module regardless of Go. We want to have an async function on the native side that gets variables such as iteration count, size, mode and number of workers and a callback, pass these to our Process function, here called GoPrimerProcess which is automatically generated for us, again it correlates with package name and function name, prefixed by “Go”.

That’s basically it. The Javascript part now kicks in, and again we’ll do this like any other native module glue:

Android

For Android, this will be even easier because we have Android Studio support through a Gradle plugin. Remember, we still have our same Go package implemenation, untouched, in the same place. We need to specify that location in Gradle and wire the Android native bridge in Java.

These would be the same steps to do in any Android and Go app. For more information you can look at the reference project and here.

Like every Android native component in React Native, we have our module which contains the interesting bits and the package which instructs React Native to inject our module in.

Now, our bridge code. The module part:

And the package part:

Our Java module code shows that the Go package magically appears in go.primer and under a pseudo-object called Primer (how convenient!). Note that gomobile generates the Go side for you and is idiomatically lays it like a real Java module.

This completes a grand-tour around: Go, iOS (bridge), Javascript (glue), and Android (bridge).

A Grand Tour

This has been a tour around four languages, doing plumbing work: In Go, we wired the real Go module to make a function which we can export cleanly. On iOS, we built a native module and bridged it to the native library, which isn’t different than any other C library for that matter. In Javascript, we wired the native part to application-side code which isn’t unlike anything we’d do for a regular native module. Closing up, on Android, we set up a nice build configuration and a simple native module bridge.

Most of this investment in plumbing work would be the same if we were to build any native module. The difference here is that now we have a single native, Go based, codebase to work with — and for the long-run this is going to save a ton of time because we don’t need to maintain the same parallel codebases on Android and iOS.

The Performance Question

How does our code perform in Javascript compared to Go? We’ll run a silly benchmark, which probably is misleading for any real-life decision making so don’t take it as a fact and you can try it live in PrimerApp. TL;DR is that Go is 2x faster than Javascript for this and this benchmarks.

Conclusion

Go with React Native is a match made in heaven for the following scenarios:

There are certainly more motivation to match Go with React Native but I believe this small list was what you’ll meet day to day. With that, I hope you enjoyed this fusion of technologies and that you’ll come to build something interesting with the ideas I showed you here.

You can find the reference app here and the Go module here.

Also, I’m passionate about obliterating native code duplication. If you need help doing this move, drop me a line at dotan@paracode.com.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.

If you enjoyed this story, we recommend reading our latest tech stories

← all posts