React Native: Why and How to Build Your Native Code in Go
- Why Go Native?
- Using Go for Cross-Platform Mobile Development
- Our Prisma Clone: Primer
- The Go Mobile Toolchain
- Connecting React Native and Go Mobile
- A Grand Tour
- The Performance Question
- Some parts you have to build natively.
- Different platforms are different in experience.
- Different devices perform differently.
- 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?
- Low level OS access. This is already all around us. We use React Native packages that deal with the native parts of each OS, because core React Native hasn’t got to it yet, or it’s out of scope.
- Tricky async logic. We might prefer using the native OS facilities for dealing with async workflows, or prefer the “local” language constructs for doing so. Different mental models for concurrency like CSP and the actor model come to mind.
- Libraries and ecosystem. Some libraries might not be available on npm, or don’t have the quality we expect, or features, or community support. The native variant might be already gold-standard.
- Lastly, my favorite: binary communication. While you could communicate over a binary protocol such as Protobuf from your React Native process, it may be painful and challenging to unpack binary data, or for that to perform well, and for that to be maintainable or supported by existing platform and libraries.
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
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’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:
- Build it twice in Java and Obj-C.
- Once in C/C++.
- Once in Go.
Actually, for this particular use case and algorithm, we tick even more items from the “why go native” list above:
- 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.
- 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.
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):
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”.
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.
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.
A Grand Tour
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
Go with React Native is a match made in heaven for the following scenarios:
- You are passionate about Go or already have existing code in Go, or want to share backend Go code with your mobile app. Granted, specifically for that, you also have GopherJS which works exceptionally well.
- You need the performance margin.
- You want the infrastructural robustness and type safety values of Go.
- You want proprietary code to stay safe.
- You don’t want to express ideas in both Obj-C/Swift and Java.
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.
Also, I’m passionate about obliterating native code duplication. If you need help doing this move, drop me a line at email@example.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