Recipe To Make React Native Apps Blazing Fast: Turbo Modules!

Tarandeep Matharu
Simform Engineering

--

We have a new react-native architecture in the latest release, and communication with native platforms can now happen without any bridge. Thanks to JSI, developers will be able to create turbo modules. In addition, we’ve got a new renderer named Fabric, exceptional direct communication with native, the ability to use multiple JS engines, concurrency, and many exciting features.

So let’s see what’s interesting to learn! 🤩

  • Problem with communication between the native and javascript world
  • What is a turbo module and what problem does it solve?
  • The billion-dollar question: What is JSI?
  • Implementing our own: TurboModule
    - Steps for the Android platform
    - Steps for the iOS platform

Problem with communication between native and javascript world

When communicating with Android/iOS native APIs, we currently pass over serialized data from the RN bridge to the respective platforms and receive data back in the same manner.

To be honest, it’s just slow, and we as developers want fast things, don’t we? In a real-world scenario, lots of operations are also done using the RN bridge. And so, it becomes a bottleneck, slowing communication between the native and javascript worlds.

The current implementation for communication is known as native modules. To eliminate this problem and to actually upgrade our slow car 🚙 to an F1 racing car 🏎 , the RN team introduced turbo modules in the new architecture.

What is a turbo module?

A turbo module is a native module created using JSI (javascript interface) technology from the new react-native architecture.

When using the turbo module, we no longer use the RN bridge, and it’s like calling a simple JavaScript function because the method from native gets registered on our JavaScript runtime. As a result, it’s blazing fast. And that’s why the RN team called it the turbo module, cause it’s fast, like really fast! 😎

With turbo modules, we get type safety across all platforms, support for more types and even custom ones, and a fast communication medium, which is the best of all. Of course, there are some more steps to implementing it than a native module implementation, but it’s worth it in the end.

The billion-dollar question: What is JSI? 🤑💰💸

Gif from GIPHY by @billionbackrecords

JSI is an API for virtually any JavaScript virtual machine that helps to directly and synchronously invoke methods on the native side (java or/objc).

Above is how the react-native team defines JSI. Pretty confusing, isn’t it? 🤨 What happens is that our native methods get registered on the currently running JavaScript engine that RN is working on so that we can just directly invoke them like this:

MessageModule.sendMessage(‘Jimmy’, ‘Hi how are you?’);

There is no RN bridge 🤯, which makes it blazing fast now. 🚀

Implementing our own: TurboModule

Let’s start with TurboModule. Below, we illustrate the steps to creating an RN project, which is the obvious first step 😉. And then, we install react-native-codegen, which will generate native boilerplate code for us!

  • Step 1: Create an RN project of the latest release
npx react-native init turbocalendar//also execute this in ios folder if cocoapods weren't installed properly. Make sure that cocoapods are installed in ios folder.pod install
  • Step 2: Install react-native-codegen
npm install react-native-codegen

🔷 Steps for Android Platform:

Before starting, please use Android Studio to write the turbo module and install NDK because there will be c++ involved. This version of NDK should be downloaded and installed.

Install NDK from SDK manager
  • Step 3: Enable RN new architecture from android/gradle.properties
//Look for this option and set it to true from false
newArchEnabled=true
  • Step 4: Enable Hermes JavaScript engine inside android/app/build.gradle
// change enableHermes to true from false
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
]
  • Step 5: Install react-native-gradle-plugin
npm install react-native-gradle-plugin
  • Step 6: Build the project on android
npx react-native run-android
  • Step 7: Provide a Flow js Module schema in moduleschema/NativeCalendarModule.js
  • Step 8: Apply react-native-gradle-plugin in android/app/build.gradle
// paste this in top lines of the file
apply plugin: "com.facebook.react"
  • Step 9: Give react config object in android/app/build.gradle for react-native-gradle-plugin to use and generate necessary files.
// Paste this object after applying react-native-gradle-plugin
react {
libraryName = "calendarlibrary"
jsRootDir = rootProject.file("../moduleschema/")
codegenDir = rootProject.file("../node_modules/react-native- codegen/")
}
  • Step 10: Uncomment generateCodegenArtifactsFromSchema task from afterEvaluate block of android config object in android/app/build.gradle
afterEvaluate { // If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs) preReleaseBuild.dependsOn(packageReactNdkReleaseLibs) ...}
  • Step 11: Inside android/app/src/main/jni/Android.mk read through the comments and uncomment the lines which are told to.
  • Step 12: Add libreact_codegen_calendarlibrary to LOCAL_SHARED_LIBRARIES variable inside Android.mk the file.

⚠️ Warning: Do not remove any other library from here; these are c++ libraries.

  • Step 13: Re-run the project on Android. You will see codegen generated files in android/app/build/generated/source/codegen a folder like this
Code generated by react-native-codegen for turbo module

We’ll use NativeCalendarModuleSpec.java file to extend from it and create our NativeCalendarModule.java.

  • Step 14: Create a NativeCalendarModule.java file inside app/src/main/java/com/turbocalendar the folder. Look at the code below, which extends NativeCalendarModuleSpec.java.

Provide created turbo module inside MainApplicationReactNativeHost.java like below ⬇️

  • Step 15: Provide the turbo module's library inside android/app/src/main/jni/MainApplicationModuleProvider.cpp
  • Step 16: Paste the following code into App.js

In the above code, we are importing the NativeCalendarModule from the schema file we created, so we get type safety here as well 😀 also it’s essential to do this way.

Step 17: Run the project on Android; click on click me!, the button in UI; after clicking the button, you will receive a message.

Here is the Android output:

Android Output

Finally, we created a turbo module, called a method from it, and received a response. 🎉

This is how we create a turbo module implementing generated spec files. It’s up to you which API you want to consume. It’s just like native modules.

GIF from GIPHY

Awesome! We have implemented the turbo module for Android. Grab your cup of coffee ☕️ , and let’s move forward to the iOS steps.

🔷 Steps for iOS Platform:
As we have done with Android, the iOS one isn’t that complex. Also, ensure to use Xcode; it will give autosuggest and a lot of other stuff to make our lives easier.

📘 Note: If you have already performed steps 1 and 2, you can skip them.

  • Step 3: Provide module schema config in the package.json file for react-native-codegen to use.
// Place this object in package.json file
"codegenConfig": {
"libraries": [
{
"name": "NativeCalendarModule",
"type": "modules",
"jsSrcsDir": "./moduleschema"
}
]
}
  • Step 4: Enable New Architecture by executing this command in the ios folder.
RCT_NEW_ARCH_ENABLED=1 bundle exec pod installif above doesn't work for m1 macs usearch -x86_64 RCT_NEW_ARCH_ENABLED=1 bundle exec pod install

This will enable the new architecture, install required pods, and generate necessary files using the module schema folder’s NativeCalendarModule.

  • Step 5: In moduleschema/NativeCalendarModule.js, include a Flow js Module schema.
  • Step 6: Because there will be c++ involved, we’ll use the .mm file extension, which allows us to use objc with c++. In the turbocalendar folder, create a header file called RCTNativeCalendarModule.
Xcode project files view

Then in that file, paste the following code:

  • Step 7: Provide an implementation for this file by creating a RCTNativeCalendarModule.mm file and using this code inside that file.

The above code provides the implementation and has a getMessage method, if you notice, which will be called from JavaScript directly without any bridge, just like calling another JavaScript method.

  • Step 8: Inside AppDelegate.mm file’s didFinishLaunchingWithOptions method, add this statement as the first line:
RCTEnableTurboModule(YES);

We are now good to go for iOS, too, when you run your app and click the click me! If you follow the method, you’ll receive the same message we received when running the app on Android.

Here is the iOS output:

iOS Output
Gif from GIPHY

Hurray! We have created a turbo module for both the Android and iOS platforms. By the way, is the coffee finished yet? 😉

Find the source code on GitHub:
https://github.com/tarandeep-simform/turbocalendar/tree/master

Conclusion ⛳️

React Native’s new architecture has given us game-changing tools for cutting-edge development. The Turbo modules make our communication with the native world from the JavaScript world blazing fast.

Giving us type safety across all platforms, support for new types, support for custom types, and direct native API method calls without involving the RN bridge. So cool, right? ☃️

Using the turbo modules system, we can make our app’s communication with the native world (Android or iOS) much faster and more efficient. I hope that you use the power 🔋 of this new method; and lastly

May, the React Native Power be with you. Good luck!

--

--