React Native & the JavaScript Interface: A Guide for Early Adopters

Part 1 — Setting up the JavaScript Interface with iOS

Daniel Friyia
7 min readMay 16, 2022
Photo by Jake Walker on Unsplash

My journey as a programmer began around the time smartphones became popular. I can still remember the excitement I felt compiling my first hello world app on an Android phone at university. As my career progressed, I worked with Bluetooth Low Energy, and C++ libraries, and wrote highly optimized apps. Nothing is as exciting to me as getting the most I can out of the hardware I am working on.

In React-Native, this was hard to do for a long time because we had to pass values back and forth across the JavaScript bridge. Fortunately, the team at Meta recently exposed JavaScript Interface (JSI) modules. JSI Modules can call native functions synchronously through shared memory.

Although JSI Modules are not yet in a state where they are ready for general consumption, they are powerful enough to experiment with and prepare for when the real thing is released.

This article is the first of a two-part series. The goal of this series is to make a quick, easy, copy-paste guide to JSI modules so you can start experimenting with them as soon as possible. As far as I can tell, nothing like this exists in the community and is sorely needed.

Part 1 is based on the YouTube playlist by Oscar Franco on setting up JSI with iOS. If you want a long, in-depth explanation of things, I recommend you go here and check out his channel.

Part 2 no one has talked about as far as I can tell. This is setting up JSI Modules for Android. Never fear though, I’ve labbed 🧑‍🔬 out this process for you and will explain everything there.

Remember, if you get stuck, check out the GitHub project here.

What are we building today?

As always, I want to explain what we are building before we build it. Today’s project will seem, at face value, a little boring. That being said, this project is really about learning to set up the JSI. Once you have that knowledge you can make modules as complex as you like.

Here we are creating an app to calculate the Greatest Common Denominator using a C++ function that will be shared between Android and iOS.

Our app running on iOS

Generating the Library Boilerplate

Currently, there is no easy way to generate a JSI library. Instead, what we are going to do is generate a JavaScript bridge library and migrate it over to a JSI lib. The first thing you’ll want to do is run this command:

npx create-react-native-library react-native-teaching-jsi

You can just hit enter until you get to this menu:

Make sure you select C++ for Android and iOS here. This will make sure all the C++ code we write is fully cross-platform.

Remove Flipper for Faster Compilation (Optional)

Before starting, I recommend you comment on Flipper in your example/Podfile. It has a lot of broken releases and sometimes causes things to not compile. When you are done with the tutorial you can always circle back and try to set it up if you feel inclined.

Making the Cross-Platform Code

After generating the template, you’ll want to start by running yarn in the root of your project to install all the dependencies. Next run open example/ios/TeachingJsiExample.xcworkspace from the project root to open Xcode in the project example. It will seem strange to most but we’ll be editing our Pods from there. We do this so Xcode can give us auto-complete functionality. I recommend trying to run the app from Xcode at this point to make sure everything is working properly. You should see a blank screen with the text “Result: 21”.

Next, find the library files in Xcode under Pods/Development Pods. It will look like this.

Update the contents of the react-native-teaching-jsi.h file to the following:

#ifndef react_native_teaching_jsi_h
#define react_native_teaching_jsi_h
#include <jsi/jsilib.h>
#include <jsi/jsi.h>
using namespace facebook;void installMath(jsi::Runtime &rt);#endif /* my_cpp_module_hpp */

Next, move over to the react-native-teaching-jsi.cpp file. Here we are going to write our shared function to calculate Greatest Common Denominator in C++. Pretty cool right?

Let’s start by using the facebook namespace and writing a fast GCD function. It will look like this.

#include "react-native-teaching-jsi.h"using namespace facebook;int computeGcd(int a, int b) {
while (b) b ^= a ^= b ^= a %= b;
return a;
}

Surely by now, you must be curious. How do we tell JavaScript/TypeScript that this function exists? The way this happens is by getting access to the JavaScript runtime and “installing” the various functions you want to use in the JS Global object. Before we do that though we need to create the install function that registers compute gcd.

Add this function to your react-native-teaching-jsi.h file.

I am sure that function is quite a lot to digest 😅 so let's break it down.

void installMath(jsi::Runtime & rt) {

The function header takes in a reference to the jsi::Runtime. This is required to access the JS engine and call our functions from JavaScript.

auto propId = jsi::PropNameID::forAscii(rt, "gcd");

The prop id is just a unique id for our function.

auto lamda = [](jsi::Runtime & rt,
const jsi::Value & thisValue,
const jsi::Value * args,
size_t count
) -> jsi::Value {
auto data = args->asObject(rt);
auto aValue = data.getProperty(rt, "a");
auto bValue = data.getProperty(rt, "b");
int a = aValue.asNumber();
int b = bValue.asNumber();
int gcd = computeGcd(a, b);
return jsi::Value(gcd);
};

The code above is where things get interesting. This C++ lambda function extracts data from the JavaScript Interface and converts it into primitive values we can use. We use the utility methods asObject and getProperty to convert the raw data into data types that can be processed by C++.

jsi::Function gcd = 
jsi::Function::createFromHostFunction(rt, propId, 2, lamda);
rt.global().setProperty(rt, "jsiGcd", gcd);

We then finish up by calling rt.global() which tells JavaScript we want to register this function on the global module.

Registering the Function on the JavaScript Runtime

The last step we need to take before calling this function from JavaScript is to install our function in the JavaScript runtime. We need to write a little bit of Objective-C code here. This Obj-C code is really just the glue between C++ and JavaScript.

Under the iOS directory, find the TeachingJsi.h file and add the following code:

@interface TeachingJsi : NSObject <RCTBridgeModule>
@property(nonatomic, assign) BOOL setBridgeOnMainQueue;
@end

This tells Objective-C you will be registering the function on the main thread which is required for the JSI to work.

Once you’ve completed this go into your TeachingJsi.mm file. Start by adding these imports:

#import <React/RCTBridge+Private.h>
#import <React/RCTUtils.h>
#import <jsi/jsi.h>
#import <ReactCommon/CallInvoker.h>
#import <memory>

Then under @implementation add this code:

@synthesize bridge = _bridge;

After exporting the module you’ll want to tell Objective-C that you will be registering your method on the main thread:

+ (BOOL)requiresMainQueueSetup {
return YES;
}

Next set up the bridge:

- (void)setBridge:(RCTBridge *)bridge {
_bridge = bridge;
_setBridgeOnMainQueue = RCTIsMainQueue();
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;

if(!cxxBridge.runtime) {
return;
}
installMath(*(facebook::jsi::Runtime *) cxxBridge.runtime);
}

Finish off by deleting all other code in the file. Everything together should look like this:

Finishing things off

The last thing you’ll want to do is check that all the code we’ve written so far works as expected. Since this isn’t an article on how to make UIs, just copy and paste my UI code into your App.tsx.

Your final result should be something like this:

Conclusion

And there you have it! Setting up JSI with iOS. Stay tuned for Part 2 where I’ll be going over how to get this to work with Android. Fair warning, that article isn’t going to be anywhere near this easy. If you haven’t worked with the Java Native Interface (JNI) or CMakeLists before it’ll be a bit of a ride. I promise though that it’ll be fun and really interesting 🙂. Look forward to seeing you there!

More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Check out our Community Discord and join our Talent Collective.

--

--

Daniel Friyia

Daniel is an Agile Software Developer specializing in all forms of mobile development, Native iOS, React-Native and beyond