How to Support New React Native Architecture in your Mobile App?

React Native Architecture migration guide. It explains how to deploy the new Architecture, including the new NativeModule system (TurboModule) and the new Renderer (Fabric), to iOS and Android libraries and apps.

Support New React Native Architecture in your Mobile App

This migration guide is intended for authors of React Native libraries and application developers. It explains how to deploy the new Architecture for react native mobile app development, including the new NativeModule system (TurboModule) and the new Renderer (Fabric), to iOS and Android libraries and apps.

Prerequisites for Applications

It must meet a few prerequisites before enabling the new architecture in the application.

Use a React Native nightly release

You must now use a React Native nightly release to get the most recent updates. We will eventually suggest aiming for a minimal stable open-source release.

This article is prepared, assuming you’re running a specific nightly release. The intended nightly release may be updated when new revisions of this guide are available. The nightly version that we’ll utilize for the rest of this guide is 0.0.0-20220201-2008-79975d146.

We recommend updating your app to the most recent open-source version before upgrading to a specific nightly release. By initially updating to a stated open-source release, you would be able to use tools such as the upgrade helper to evaluate what extra modifications may be needed for your project.

The most recent stable release is 0.68.2 as of this writing. After successfully upgrading your project to this version, you may target the 0.0.0-20220201-2008-79975d146 nightly release. You can target this nightly release in the same way you would any other React Native version:

yarn add react-native@0.0.0-20220201-2008-79975d146

Install react-native-codegen​

Check that you are using the most recent version of the react-native-codegen NPM package. It is 0.0.13 at the time of this writing.

yarn add react-native-codegen

Android specifics

There are various prerequisites for using the new architecture on Android:

  1. Gradle 7.x and the Android Gradle Plugin 7.x is being used.
  2. Making use of the new React Gradle Plugin
  3. Creating React-native from the Source

You may upgrade Gradle by running:

cd android && ./gradlew wrapper –gradle-version 7.3 –distribution-type=all

While it must change the AGP version at the com.android.tools. Build:Gradle dependency line in the top-level build.gradle file.

If you’re ready, let’s install the new Gradle plugin, available as an NPM package named react-native-Gradle-plugin. You may accomplish so by using:

yarn add react-native-Gradle-plugin

You may check if the package is already installed by doing the following:

ls -la node_modules/react-native-Gradle-plugin

You may now change your top-level settings.Gradle file to add the following line at the bottom of the file:

includeBuild(‘../node_modules/react-native-gradle-plugin’)
include(“:ReactAndroid”)
project(“:ReactAndroid”).projectDir = file(‘../node_modules/react-native/ReactAndroid’)

Then, in the top-level Gradle file, add the following lines:

buildscript {
// …dependencies {// Make sure that AGP is at least at version 7.xclasspath(“com.android.tools.build:gradle:7.0.4”)// Add those linesclasspath(“com.facebook.react:react-native-gradle-plugin”)classpath(“de.undercouch:gradle-download-task:4.1.2”)}}

Include the following in your module-level Gradle file (typically app/build.gradle[.kts]):

apply plugin: “com.android.application

// Add those lines
apply plugin: “com.facebook.react”// Add those lines as wellreact {
reactRoot = rootProject.file(“../node_modules/react-native/”)
codegenDir = rootProject.file(“../node_modules/react-native-codegen/”)}

Finally, change your project to utilize the react-native requirement from the source instead of a pre-compiled object from the NPM package. It is required since the subsequent setup will depend on creating native code using the source.

Let’s update the following line in your module-level build.Gradle (the one in the app/ folder):

dependencies {
// Replace this:implementation “com.facebook.react:react-native:+”  // From node_modules// With this:implementation project(“:ReactAndroid”)  // From node_modules

Use Hermes

​Hermes is an open-source JavaScript engine designed specifically for React Native. We strongly advise you to use Hermes in the application. With Hermes configured, you would be able to debug your JavaScript code directly in Flipper using the JavaScript debugger.

iOS: Enable C++17 language feature support

To enable C++17 language features, you must change your Xcode project settings.

Instructions

  1. In the Project Navigator on the left, select your project (e.g., MyXcodeApp)
  2. Then, in the middle pane, check sure your project is chosen.
  3. Look for C++ Language Dialect or CLANG CXX LANGUAGE STANDARD in Build Settings.
  4. Select C++17 from the dropdown menu (or type “c++17” straight into the value field).

If you did everything successfully, your diff should display the following modifications to your project file: CLANG_CXX_LANGUAGE_STANDARD = “c++17”

Folly should also be enabled in your project, and it should occur instantly after the library requirement is detected. Thus no more modifications to your project are required.

iOS: Use Objective-C++ (.mm extension)

Objective-C or C++ can be used to create TurboModules. Any source files containing C++ code should be using the.mm file extension to handle both instances. This extension is equivalent to Objective-C++, a language variation that supports the usage of C++ and Objective-C in source files.

To guarantee that file references remain in your project, use Xcode to rename existing files. Before rebuilding the program, you may need to clear the build folder (Project Clean Build Folder). If the file is changed outside of Xcode, you may have to locate the new file by clicking on the old.m file references.

iOS: TurboModules: Make sure your App Offers an RCTCxxBridgeDelegate

​To configure the TurboModule system, add more code to your AppDelegate that interacts with the bridge. Before you begin, rename your AppDelegate file for using the.mm extension.

Your AppDelegate will now comply with RCTCxxBridgeDelegate. Begin by including the following imports at the beginning of your AppDelegate file:

#import <reacthermes/HermesExecutorFactory.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTJSIExecutorRuntimeInstaller.h>

Then, as an RCTCxxBridgeDelegate provider, declare your app delegate:

@interface AppDelegate () <RCTCxxBridgeDelegate> { // … } @end

You must implement the jsExecutorFactoryForBridge: function to comply with the RCTCxxBridgeDelegate protocol. Typically, you should get a JSCExecutorFactory or HermesExecutorFactory here, and we will use it later to install our TurboModules bindings.

You may use the jsExecutorFactoryForBridge: method as follows:

#pragma mark – RCTCxxBridgeDelegate
– (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge{return std::make_unique<facebook::react::HermesExecutorFactory>(facebook::react::RCTJSIExecutorRuntimeInstaller([bridge](facebook::jsi::Runtime &runtime) {if (!bridge) {return;}}));}

Enabling the New NativeModule System (TurboModule) in your App

Android

1. Enable NDK and the native build

The code-gen will generate some Java and C++ code, which we must now build.

Let’s go through your module-level build.gradle to add the following two externalNativeBuild blocks within the android block:

android { defaultConfig {applicationId “com.awesomeproject”// …
// Add this block
externalNativeBuild {ndkBuild {arguments “APP_PLATFORM=android-21″,”APP_STL=c++_shared”,”NDK_TOOLCHAIN_VERSION=clang”,”GENERATED_SRC_DIR=$buildDir/generated/source”,”PROJECT_BUILD_DIR=$buildDir”,”REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid”,”REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build”cFlags “-Wall”, “-Werror”, “-fexceptions”, “-frtti”, “-DWITH_INSPECTOR=1″cppFlags “-std=c++17″targets “myapplication_appmodules” }}}
// Add this block
externalNativeBuild { ndkBuild { path “$projectDir/src/main/jni/Android.mk” } } }

Let’s add the following part to the same build.gradle file, under the same android:

android {
// …def reactAndroidProjectDir = project(‘:ReactAndroid’).projectDirdef packageReactNdkLibs = tasks.register(“packageReactNdkLibs”, Copy) {dependsOn(“:ReactAndroid:packageReactNdkLibsForBuck”)dependsOn(“generateCodegenArtifactsFromSchema”)from(“$reactAndroidProjectDir/src/main/jni/prebuilt/lib”)into(“$buildDir/react-ndk/exported”)}afterEvaluate {preBuild.dependsOn(packageReactNdkLibs)configureNdkBuildDebug.dependsOn(preBuild)configureNdkBuildRelease.dependsOn(preBuild) }packagingOptions {pickFirst ‘**/libhermes.so’pickFirst ‘**/libjsc.so’ } }

Finally, under the src/main/jni folder, create a Makefile named Android.mk with the following points:

THIS_DIR := $(call my-dir)
include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
include $(GENERATED_SRC_DIR)/codegen/jni/Android.mkinclude $(CLEAR_VARS)LOCAL_PATH := $(THIS_DIR)
LOCAL_MODULE := myapplication_appmodulesLOCAL_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni# Please note as one of the library listed is libreact_codegen_samplelibrary
# This name will be generated as libreact_codegen_<library-name>
# where <library-name> is the one you specified in the Gradle configuration
LOCAL_SHARED_LIBRARIES := libjsi \
libfbjni \
libglog \
libfolly_json \
libyoga \
libreact_nativemodule_core \
libturbomodulejsijni \
librrc_view \
libreact_render_core \
libreact_render_graphics \
libfabricjni \
libfolly_futures \
libreact_debug \
libreact_render_componentregistry \
libreact_render_debug \
libruntimeexecutor \
libreact_codegen_rncore \
libreact_codegen_samplelibraryLOCAL_CFLAGS := \
-DLOG_TAG=\”ReactNative\”
LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wallinclude $(BUILD_SHARED_LIBRARY)

This configuration will execute a native build on the project and compile the C++ files the codegen has generated. The native build will be running alongside the Gradle task: app:externalNativeBuildDebug

You can now test that everything is working properly by starting your Android app: yarn react-native run-android

2. Java – Provide a ReactPackageTurboModuleManagerDelegate

It’s finally time to put the TurboModule to work. First, we’ll need to make a ReactPackageTurboModuleManagerDelegate subclass similar to this:

package com.awesomeproject;
import com.facebook.jni.HybridData;import com.facebook.react.ReactPackage;import com.facebook.react.ReactPackageTurboModuleManagerDelegate;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.soloader.SoLoader;import java.util.List;public class MyApplicationTurboModuleManagerDelegate extends ReactPackageTurboModuleManagerDelegate {private static volatile boolean sIsSoLibraryLoaded;protected MyApplicationTurboModuleManagerDelegate(ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {super(reactApplicationContext, packages); }protected native HybridData initHybrid();public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {protected MyApplicationTurboModuleManagerDelegate build(ReactApplicationContext context, List<ReactPackage> packages) {return new MyApplicationTurboModuleManagerDelegate(context, packages); } }@Override

protected synchronized void maybeLoadOtherSoLibraries() {// Prevents issues with initializer interruptions.

if (!sIsSoLibraryLoaded) { SoLoader.loadLibrary(“myapplication_appmodules”); sIsSoLibraryLoaded = true; } } }

Please keep in mind that the SoLoader.loadLibrary argument (in this example, “myapplication appmodules”) should match the one set for LOCAL MODULE:= inside the Android.mk file you previously built.

This class will subsequently be in charge of loading the TurboModules and the native library built with the NDK during runtime.

3. Adapt your ReactNativeHost to use the ReactPackageTurboModuleManagerDelegate

The class you developed may then be sent to your ReactNativeHost. You may find your ReactNativeHost by looking for the getReactNativeHost function (). The ReactNativeHost is often found within your Application class.

Once you’ve found it, add the getReactPackageTurboModuleManagerDelegateBuilder function as seen in the code below:

public class MyApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() { /* … */ }@Overrideprotected List<ReactPackage> getPackages() { /* … */ }@Overrideprotected String getJSMainModuleName() {/* … */ }@NonNull@Overrideprotected ReactPackageTurboModuleManagerDelegate.Builder getReactPackageTurboModuleManagerDelegateBuilder() {return new MyApplicationTurboModuleManagerDelegate.Builder(); } }; }

4. Extend the getPackages() from your ReactNativeHost to use the TurboModule

On the ReactNativeHost, we need to add the newly built TurboModule to the getPackages() function. Make the following changes to the method:

public class MyApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() { /* … */ }@Overrideprotected List<ReactPackage> getPackages() {List<ReactPackage> packages = new PackageList(this).getPackages();// Add those linespackages.add(new TurboReactPackage() {@Nullable@Overridepublic NativeModule getModule(String name, ReactApplicationContext reactContext) {if (name.equals(NativeAwesomeManager.NAME)) {return new NativeAwesomeManager(reactContext);} else {return null; } }@Overridepublic ReactModuleInfoProvider getReactModuleInfoProvider() { return () -> {final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();moduleInfos.put( NativeAwesomeManager.NAME,new ReactModuleInfo( NativeAwesomeManager.NAME,”NativeAwesomeManager”, false, // canOverrideExistingModulefalse, // needsEagerInittrue, // hasConstantsfalse, // isCxxModule

true // isTurboModule ) );

return moduleInfos; }; } }); return packages; }

@Override

protected String getJSMainModuleName() {/* … */ }

@NonNull

@Override

protected ReactPackageTurboModuleManagerDelegate.Builder getReactPackageTurboModuleManagerDelegateBuilder() { return new MyApplicationTurboModuleManagerDelegate.Builder(); } };

5. C++ Provide a native implementation for the methods in your *TurboModuleDelegate class

If you look closely at the previously created class MyApplicationTurboModuleManagerDelegate, you will find that some of the functions are native.

As a result, you’ll need to include certain C++ classes to implement those functions. You will require the following files, which must place in the src/main/jni folder:

The following information should be included in such files:

MyApplicationTurboModuleManagerDelegate.h

Please keep in mind that the kJavaDescriptor should be modified to match the package name you choose for your project.

#include <memory>

#include <string>

#include <ReactCommon/TurboModuleManagerDelegate.h>

#include <fbjni/fbjni.h>

namespace facebook { namespace react { class MyApplicationTurboModuleManagerDelegate : public jni::HybridClass<MyApplicationTurboModuleManagerDelegate, TurboModuleManagerDelegate> { public:

// Adapt it to the package you used for your Java class.

static constexpr auto kJavaDescriptor = “Lcom/awesomeproject/MyApplicationTurboModuleManagerDelegate;”;

static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);

static void registerNatives();

std::shared_ptr<TurboModule> getTurboModule(const std::string name, const std::shared_ptr<CallInvoker> jsInvoker) override;

std::shared_ptr<TurboModule> getTurboModule(const std::string name, const JavaTurboModule::InitParams &params) override;

private:

friend HybridBase;

using HybridBase::HybridBase; }; } // namespace react } // namespace facebook

MyApplicationTurboModuleManagerDelegate.cpp

#include “MyApplicationTurboModuleManagerDelegate.h”
#include “MyApplicationModuleProvider.h”namespace facebook {namespace react {jni::local_ref<MyApplicationTurboModuleManagerDelegate::jhybriddata> MyApplicationTurboModuleManagerDelegate::initHybrid(jni::alias_ref<jhybridobject>) {return makeCxxInstance();}void MyApplicationTurboModuleManagerDelegate::registerNatives() {registerHybrid({makeNativeMethod(“initHybrid”, MyApplicationTurboModuleManagerDelegate::initHybrid), }); }std::shared_ptr<TurboModule> MyApplicationTurboModuleManagerDelegate::getTurboModule(const std::string name, const std::shared_ptr<CallInvoker> jsInvoker) {// Not implemented yet: provide pure-C++ NativeModules here.return nullptr; }std::shared_ptr<TurboModule> MyApplicationTurboModuleManagerDelegate::getTurboModule(const std::string name, const JavaTurboModule::InitParams &params) {return MyApplicationModuleProvider(name, params); } } // namespace react } // namespace facebook

MyApplicationModuleProvider.h

#pragma once
#include <memory>#include <string>#include <ReactCommon/JavaTurboModule.h>namespace facebook {namespace react {std::shared_ptr<TurboModule> MyApplicationModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params);} // namespace react} // namespace facebook

MyApplicationModuleProvider.cpp

Please modify the samplelibrary.h import to use the same library name that you used while building the applications. It is the C++-created file produced by codegen.

If you have more than one TurboModule, you can select more than one supplier here. In this case, we specifically seek a TurboModule from the sample library (the one we provided) and drop it back to the rncore Module Provider.

#include “MyApplicationModuleProvider.h”
#include <rncore.h>#include <samplelibrary.h>namespace facebook {namespace react {std::shared_ptr<TurboModule> MyApplicationModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params) {auto module = samplelibrary_ModuleProvider(moduleName, params);if (module != nullptr) {return module; }return rncore_ModuleProvider(moduleName, params); }} // namespace react} // namespace facebook

OnLoad.cpp

#include <fbjni/fbjni.h>
#include “MyApplicationTurboModuleManagerDelegate.h”JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {return facebook::jni::initialize(vm, [] {facebook::react::MyApplicationTurboModuleManagerDelegate::registerNatives();});}

6. Enable the useTurboModules flag in your Application onCreate

Finally, you may enable TurboModule functionality in the application. To do so, enable the useTurboModule flag in the Application onCreate function.

public class MyApplication extends Application implements ReactApplication {
@Overridepublic void onCreate() {ReactFeatureFlags.useTurboModules = true;//…}

It’s now time to re-run your Android app to ensure that everything is working correctly: yarn react-native run-android

iOS

1. Provide a TurboModuleManager Delegate

Include the following imports at the start of the bridge delegate (for example, AppDelegate.mm):

#import <ReactCommon/RCTTurboModuleManager.h>
#import <React/CoreModulesPlugins.h>

You must also specify that the AppDelegate follows the RCTTurboModuleManagerDelegate protocol and add an instance variable for the Turbo Module manager:

@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
// … RCTTurboModuleManager *_turboModuleManager; } @end

To comply with the RCTTurboModuleManagerDelegate standard, you must implement the following three methods:

TurboModuleManagerDelegate Example

Take notice of getModuleInstanceFromClass: in the following cases since it contains the required instantiation of numerous essential modules that you’ll need to include within your application. It may not be necessary for the long run.

AppDelegate.mm
// …#import <React/RCTDataRequestHandler.h>#import <React/RCTHTTPRequestHandler.h>#import <React/RCTFileRequestHandler.h>#import <React/RCTNetworking.h>#import <React/RCTImageLoader.h>#import <React/RCTGIFImageDecoder.h>#import <React/RCTLocalAssetImageLoader.h>#import <React/CoreModulesPlugins.h>#import <ReactCommon/RCTTurboModuleManager.h>// …#pragma mark RCTTurboModuleManagerDelegate- (Class)getModuleClassFromName:(const char *)name {return RCTCoreModulesClassProvider(name); }- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)namejsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker { return nullptr; }- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass {// Set up the default RCTImageLoader and RCTNetworking modules.if (moduleClass == RCTImageLoader.class) { return [[moduleClass alloc] initWithRedirectDelegate:nilloadersProvider:^NSArray<id<RCTImageURLLoader>> *(RCTModuleRegistry * moduleRegistry) {return @ [[RCTLocalAssetImageLoader new]]; } decodersProvider:^NSArray<id<RCTImageDataDecoder>> *(RCTModuleRegistry * moduleRegistry) { return @ [[RCTGIFImageDecoder new]]; }];} else if (moduleClass == RCTNetworking.class) { return [[moduleClass alloc]initWithHandlersProvider:^NSArray<id<RCTURLRequestHandler>> *( RCTModuleRegistry *moduleRegistry) {

return @[ [RCTHTTPRequestHandler new], [RCTDataRequestHandler new], [RCTFileRequestHandler new], ]; }]; }

// No custom initializer here.

return [moduleClass new]; }

2. Install TurboModuleManager JavaScript Bindings

Next, in the bridge delegate’s jsExecutorFactoryForBridge: function, build an RCTTurboModuleManager and install the JavaScript bindings:

pragma mark – RCTCxxBridgeDelegate
– (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge{// Add these lines to create a TurboModuleManagerif (RCTTurboModuleEnabled()) {_turboModuleManager =[[RCTTurboModuleManager alloc] initWithBridge:bridgedelegate:selfjsInvoker:bridge.jsCallInvoker];// Necessary to allow NativeModules to lookup TurboModules[bridge setRCTTurboModuleRegistry:_turboModuleManager];if (!RCTTurboModuleEagerInitEnabled()) {/*** Instantiating DevMenu has the side effect of registering* shortcuts for CMD + d, CMD + i,  and CMD + n via RCTDevMenu.* Therefore, when TurboModules are enabled, we must manually create this* NativeModule. */[_turboModuleManager moduleForName:”DevMenu”]; } }// Add this line…__weak __typeof(self) weakSelf = self;// If you want to use the `JSCExecutorFactory,` remember to add the `#import<React/JSCExecutorFactory.h>`// import statement on top.return std::make_unique<facebook::react::HermesExecutorFactory>(

facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) {

if (!bridge) { return; }

// And add these lines to install the bindings…

__typeof(self) strongSelf = weakSelf;

if (strongSelf) { facebook::react::RuntimeExecutor syncRuntimeExecutor = [&](std::function<void(facebook::jsi::Runtime & runtime_)> &&callback) { callback(runtime); };

[strongSelf->_turboModuleManager installJSBindingWithRuntimeExecutor:syncRuntimeExecutor]; } })); }

3. Enable TurboModule System

Finally, activate TurboModules in the app by running the following sentence in your app delegate before React Native is started (e.g., within didFinishLaunchingWithOptions:):

RCTEnableTurboModule(YES);

Example

@implementation AppDelegate

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{ RCTEnableTurboModule(YES);

RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self

launchOptions:launchOptions];

// …

return YES; }

Enabling the New Renderer (Fabric) in your App

Android

1. Provide a JSIModulePackage inside your ReactNativeHost

To enable Fabric in your project, you must include a JSIModulePackage within your ReactNativeHost. If you followed the TurboModule part of this guide, you may find your ReactNativeHost by looking for the getReactNativeHost function (). The ReactNativeHost is often found within your Application class.

Once you’ve found it, add the getJSIModulePackage function as seen in the code below:

MyApplication.java
public class MyApplication extends Application implements ReactApplication {private final ReactNativeHost mReactNativeHost =new ReactNativeHost(this) {// Add those lines:@Nullable@Overrideprotected JSIModulePackage getJSIModulePackage() {return new JSIModulePackage() {@Overridepublic List<JSIModuleSpec> getJSIModules(final ReactApplicationContext reactApplicationContext,final JavaScriptContextHolder jsContext) {final List<JSIModuleSpec> specs = new ArrayList<>();specs.add(new JSIModuleSpec() {@Overridepublic JSIModuleType getJSIModuleType() {return JSIModuleType.UIManager; }@Overridepublic JSIModuleProvider<UIManager> getJSIModuleProvider() { final ComponentFactory componentFactory = new ComponentFactory();CoreComponentsRegistry.register(componentFactory);final ReactInstanceManager reactInstanceManager = getReactInstanceManager();ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(reactInstanceManager.getOrCreateViewManagers(

reactApplicationContext));

return new FabricJSIModuleProvider( reactApplicationContext, componentFactory, new EmptyReactNativeConfig(), viewManagerRegistry); } }); return specs; } }; } }; }

2. Make sure your call setIsFabric on your Activity’s ReactRootView

Make sure you use setIsFabric on the ReactRootView within your Activity class. You may need to build a ReactActivityDelegate.

public class MainActivity extends ReactActivity {
// Add the Activity Delegate if you don’t have one already.public static class MainActivityDelegate extends ReactActivityDelegate {public MainActivityDelegate(ReactActivity activity, String mainComponentName) {super(activity, mainComponentName);}@Overrideprotected ReactRootView createRootView() {ReactRootView reactRootView = new ReactRootView(getContext());// Make sure to call setIsFabric(true) on your ReactRootViewreactRootView.setIsFabric(true);return reactRootView; } }// Make sure to override the `createReactActivityDelegate()` method.@Overrideprotected ReactActivityDelegate createReactActivityDelegate() { return new MainActivityDelegate(this, getMainComponentName()); } }

The reactRootView.setIsFabric(true) call is critical in this code because it enables the new renderer for such an Activity.

You can now test that everything is working correctly by starting your Android app: yarn react-native run-android

To prove that Fabric is functioning correctly, you should now see the following log in your Metro terminal log:

BUNDLE ./App.js

LOG Running “App” with {“fabric”:true,”initialProps”:{},”rootTag”:1}

Migrating Android ViewManagers

To begin, ensure that you follow the Enabling the New Renderer (Fabric) guidelines in the Android Application. Furthermore, we will presume that you followed the rules in Enabling the New NativeModule System (TurboModule) in the Android Application because the Makefile (Android.mk) or other native build setup stages are covered there and will not be covered here.

JavaScript changes

import codegenNativeComponent from

‘react-native/Libraries/Utilities/codegenNativeComponent’;

// babel-plugin-codegen will replace the function call to use NativeComponentRegistry

// ‘RCTWebView’ is interopped by RCTFabricComponentsPlugins

export default (codegenNativeComponent<NativeProps>(

‘RCTWebView’,

): HostComponent<NativeProps>);

RNTMyNativeViewNativeComponent.js

import type {Int32} from ‘react-native/Libraries/Types/CodegenTypes’;

import codegenNativeComponent from ‘react-native/Libraries/Utilities/codegenNativeComponent’;

import type {HostComponent} from ‘react-native’;

import type {ViewProps} from ‘react-native/Libraries/Components/View/ViewPropTypes’;

type NativeProps = $ReadOnly<{|

…ViewProps, // This is required.

someNumber: Int32,

|}>;

[…]

export default (codegenNativeComponent<NativeProps>(

‘RNTMyNativeView’, ): HostComponent<NativeProps>);

Native/Java Changes

1. Update (or create) the ViewManager to use the Codegen-generated classes.

You must specifically design the created ViewManagerInterface and provide events to the generated ViewManagerDelegate. Your ViewManager might follow this structure. In this case, the MyNativeView class is an Android View equivalent (like a subclass of LinearLayout, Button, TextView, etc.)

MyNativeViewManager.java
// View manager for MyNativeView components.@ReactModule(name = MyNativeViewManager.REACT_CLASS)public class MyNativeViewManager extends SimpleViewManager<MyNativeView>implements RNTMyNativeViewManagerInterface<MyNativeView> {public static final String REACT_CLASS = “RNTMyNativeView”;private final ViewManagerDelegate<MyNativeView> mDelegate;public MyNativeViewManager() {mDelegate = new RNTMyNativeViewManagerDelegate<>(this); }@Nullable@Overrideprotected ViewManagerDelegate<MyNativeView> getDelegate() {return mDelegate; }@NonNull@Overridepublic String getName() {return REACT_CLASS; }@NonNull@Overrideprotected MyNativeView createViewInstance(@NonNull ThemedReactContext reactContext) {return new MyNativeView(reactContext); } }

2. Add your ViewManager to one of your application’s Packages.

Modify the getPackages function in the ReactNativeHost to add the following:

public class MyApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

@Override

public boolean getUseDeveloperSupport() { /* … */ }

@Override

protected List<ReactPackage> getPackages() {

List<ReactPackage> packages = new PackageList(this).getPackages();

// … other packages or `TurboReactPackage added` here…

// Add those lines.

packages.add(new ReactPackage() {

@NonNull

@Override

public List<NativeModule> createNativeModules(

@NonNull ReactApplicationContext reactContext) {

return Collections.emptyList(); }

@NonNull

@Override

public List<ViewManager> createViewManagers(

@NonNull ReactApplicationContext reactContext) {

// Your ViewManager is returned here.

return Collections.singletonList(new MyNativeViewManager()); } });

return packages; } }; }

3. Add a Fabric Component Registry

You must build a new component Registry to register your components for Fabric discovery. Let’s fill the MyComponentsRegistry file with the following information.

As you’ll see, some methods are native(), which we will write in C++ in the following section.

package com.awesomeproject;

import com.facebook.jni.HybridData;

import com.facebook.proguard.annotations.DoNotStrip;

import com.facebook.react.fabric.ComponentFactory;

import com.facebook.soloader.SoLoader;

@DoNotStrip

public class MyComponentsRegistry {

static { SoLoader.loadLibrary(“fabricjni”); }

@DoNotStrip private final HybridData mHybridData;

@DoNotStrip

private native HybridData initHybrid(ComponentFactory componentFactory);

@DoNotStrip

private MyComponentsRegistry(ComponentFactory componentFactory) {

mHybridData = initHybrid(componentFactory); }

@DoNotStrip

public static MyComponentsRegistry register(ComponentFactory componentFactory) {

return new MyComponentsRegistry(componentFactory); } }

4. Register your custom Fabric Component Registry

Finally, let’s modify the getJSIModulePackage function from the ReactNativeHost to enroll your Component Registry with the Core one:

public class MyApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

@Nullable

@Override

protected JSIModulePackage getJSIModulePackage() {

return new JSIModulePackage() {

@Override

public List<JSIModuleSpec> getJSIModules(

final ReactApplicationContext reactApplicationContext,

final JavaScriptContextHolder jsContext) {

final List<JSIModuleSpec> specs = new ArrayList<>();

specs.add(new JSIModuleSpec() {

// …

@Override

public JSIModuleProvider<UIManager> getJSIModuleProvider() {

final ComponentFactory componentFactory = new ComponentFactory();

CoreComponentsRegistry.register(componentFactory);

// Add this line just below CoreComponentsRegistry.register

MyComponentsRegistry.register(componentFactory);

// … } });

return specs; } }; } }; }

Native/C++ Changes

It is now time to write a C++ code for your MyComponentsRegistry:

1. Create a header file: MyComponentsRegistry.h

The file must be placed in the src/main/jni directory. Please keep in mind that the kJavaDescriptor must be modified to match the package name you choose for your project.

MyComponentsRegistry.h

#pragma once

#include <ComponentFactory.h>

#include <fbjni/fbjni.h>

#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>

#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>

namespace facebook {

namespace react {

class MyComponentsRegistry

: public facebook::jni::HybridClass<MyComponentsRegistry> {

public:

constexpr static auto kJavaDescriptor =

“Lcom/awesomeproject/MyComponentsRegistry;”;

static void registerNatives();

MyComponentsRegistry(ComponentFactory *delegate);

private:

friend HybridBase;

static std::shared_ptr<ComponentDescriptorProviderRegistry const>

sharedProviderRegistry();

const ComponentFactory *delegate_;

static jni::local_ref<jhybriddata> initHybrid(

jni::alias_ref<jclass>,

ComponentFactory *delegate); };

} // namespace react

} // namespace facebook

2. Create an implementation file: MyComponentsRegistry.cpp

The file should be beside ‘MyComponentsRegistry.h’ in the src/main/jni folder.

MyComponentsRegistry.cpp
#include “MyComponentsRegistry.h”#include <CoreComponentsRegistry.h>#include <fbjni/fbjni.h>#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>#include <react/renderer/components/rncore/ComponentDescriptors.h>#include <react/renderer/components/samplelibrary/ComponentDescriptors.h>namespace facebook {namespace react {MyComponentsRegistry::MyComponentsRegistry(ComponentFactory *delegate): delegate_(delegate) {}std::shared_ptr<ComponentDescriptorProviderRegistry const>MyComponentsRegistry::sharedProviderRegistry() {auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();providerRegistry->add(concreteComponentDescriptorProvider<RNTMyNativeViewComponentDescriptor>());return providerRegistry; }jni::local_ref<MyComponentsRegistry::jhybriddata>MyComponentsRegistry::initHybrid(jni::alias_ref<jclass>,ComponentFactory *delegate) {auto instance = makeCxxInstance(delegate);auto buildRegistryFunction =[](EventDispatcher::Weak const &eventDispatcher,

ContextContainer::Shared const &contextContainer)

-> ComponentDescriptorRegistry::Shared { auto registry = MyComponentsRegistry::sharedProviderRegistry()

->createComponentDescriptorRegistry( {eventDispatcher, contextContainer});

auto mutableRegistry = std::const_pointer_cast<ComponentDescriptorRegistry>(registry);

mutableRegistry->setFallbackComponentDescriptor(

std::make_shared<UnimplementedNativeViewComponentDescriptor>(

ComponentDescriptorParameters{

eventDispatcher, contextContainer, nullptr}));

return registry; };

delegate->buildRegistryFunction = buildRegistryFunction; return instance; }

void MyComponentsRegistry::registerNatives() { registerHybrid({

makeNativeMethod(“initHybrid”, MyComponentsRegistry::initHybrid), }); } } // namespace react } // namespace facebook

3. Load your file in the OnLoad.cpp

You must have an OnLoad.cpp file in the src/main/jni folder if you followed the TurboModule guidelines. Add the following code to load the MyComponentsRegistry class:

OnLoad.cpp

#include <fbjni/fbjni.h>
#include “MyApplicationTurboModuleManagerDelegate.h”// Add this import#include “MyComponentsRegistry.h”JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {return facebook::jni::initialize(vm, [] {facebook::react::MyApplicationTurboModuleManagerDelegate::registerNatives();// Add this linefacebook::react::MyComponentsRegistry::registerNatives(); }); }

You can now test that everything is working correctly by starting your Android app: yarn react-native run-android

iOS

1. Enable Fabric in Podfile

Changes should be made to your Podfile. Some examples may be found in RNTester & rn-demo-app.

Podfile

# Add the following line at the top of Podfile.

# Codegen produces files/classes that share names, and it will show the warning.

# deterministic_uuids option surpresses the warning.

install! ‘cocoapods’, :deterministic_uuids => false
target ‘Some App’ dopods()enddef pods()# Get configconfig = use_native_modules!# Use env variables to turn it on/off.fabric_enabled = ENV[‘USE_FABRIC’]use_react_native!(…# Modify here if your app root path isn’t the same as this one.:app_path => “#{Dir.pwd}/..”,# Pass the flag to enable fabric to use_react_native!.:fabric_enabled => fabric_enabled ) End

2. Update your root view

Your configuration determines the method you use to render your app using Fabric. Here’s an example of activating Fabric in your project using the RN FABRIC ENABLED compiler flag. As an example, consider Tester’s RN-AppDelegate.

AppDelegate.mm

#ifdef RN_FABRIC_ENABLED

#import <React/RCTFabricSurfaceHostingProxyRootView.h>

#import <React/RCTSurfacePresenter.h>

#import <React/RCTSurfacePresenterBridgeAdapter.h>

#import <react/config/ReactNativeConfig.h>

#endif

@interface AppDelegate () <RCTCxxBridgeDelegate,

RCTTurboModuleManagerDelegate> {

#ifdef RN_FABRIC_ENABLED

RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;

std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;

facebook::react::ContextContainer::Shared _contextContainer; #endif

// Find a line that defines rootView and replace/edit with the following lines.

#ifdef RN_FABRIC_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();

_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();

_contextContainer->insert(“ReactNativeConfig”, _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc]

initWithBridge:bridge

contextContainer:_contextContainer];

bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;

UIView *rootView = [[RCTFabricSurfaceHostingProxyRootView alloc] initWithBridge:bridge

moduleName:<#moduleName#>

initialProperties:@{}];

#else

// Current implementation to define rootview.

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge

moduleName:<#moduleName#>

initialProperties:@{}]; #endif

3. Add Babel Plugins

It will start the codegen, which will run during the metro building process.

babel.config.js
module.exports = {presets: [‘module:metro-react-native-babel-preset’],plugins: [‘@babel/plugin-proposal-class-properties’,’./node_modules/react-native/packages/babel-plugin-codegen’]};

4. Run pod install

// Run pod install with the flags

USE_FABRIC=1 RCT_NEW_ARCH_ENABLED=1 pod install

Conclusion

Most JavaScript application code does not need to change due to the new architecture in terms of backward compatibility. The Java/ObjC code for custom View Managers and Native Modules must be changed, although many may be code-modified to work with the new system. A compatibility layer that allows bespoke View Managers and Native Modules to continue operating in the new system may also be built.

In terms of timeframes, most of the JSI code has already been deposited in the repository while writing. A considerable percentage of the Fabric code is already in the repository, and TurboModules upgrades are still being released. It is generally backward compatible; there is no need for a single release date but rather a steady rollout.

If you need any help regarding implementing it; contact us for migration.

Exit mobile version