Use React Native Turbo Module within existing native apps

539 Views Asked by At

When using native modules, it was possible to use them within existing native apps without the necessity of creating a new module:

There are two ways to write a native module for your React Native application:

  1. Directly within your React Native application’s iOS/Android projects

  2. As a NPM package that can be installed as a dependency by your/other React Native applications (https://reactnative.dev/docs/native-modules-intro)

With Turbo Modules, this option is not mentioned at all within the documentation. I know that Turbo Modules are quite cutting-edge, but is it possible to use Turbo Modules directly within native apps, just like native modules? I'd prefer a solution that will still work with CodeGen, if there is one.

Why do I want this? I'm trying to integrate React Native into my existing iOS and Android apps (Brownfield). By using native modules directly within my native apps, I can easily access old implementations/data without having to port them over into standalone modules.

I've tried to move the implementation of the <ModuleName>Spec.java into my existing android app, but this way the class extending TurboReactPackage is not able to return a new instance as soon as getModule is called.

Edit: I got it working. See my command if you want to know how.

1

There are 1 best solutions below

2
On

By doing some tinkering I figured out, that I can just follow the documentation but do everything inside my existing react native project. I have to provide my own OnLoad.cpp and CMakeLists.txt however. This can be done by following the c++-Documentation of React Native Turbo Modules.

Here is some more detailed information on how I achieved this:

Add this to your apps build.gradle file:

android {
    ...
    externalNativeBuild {
        cmake {
            path "src/main/jni/CMakeLists.txt"
        }
    }
}

After that place the following two files under src/main/jni:

CMakeLists.txt:

# Copyright (c) Meta Platforms, Inc. and affiliates.
cmake_minimum_required(VERSION 3.13)
project(appmodules)
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)

OnLoad.cpp:

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
#include <DefaultComponentsRegistry.h>
#include <DefaultTurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncli.h>
#include <<YourModuleNameHere>Spec.h>

namespace facebook {
namespace react {

void registerComponents(
    std::shared_ptr<ComponentDescriptorProviderRegistry const> registry) {
  rncli_registerProviders(registry);
}

std::shared_ptr<TurboModule> cxxModuleProvider(
    const std::string &name,
    const std::shared_ptr<CallInvoker> &jsInvoker) {
  return nullptr;
}

std::shared_ptr<TurboModule> javaModuleProvider(
    const std::string &name,
    const JavaTurboModule::InitParams &params) {
  auto module = <YourModuleNameHere>Spec_ModuleProvider(name, params);
  if(module != nullptr) {
    return module;
  }
  return rncli_ModuleProvider(name, params);
}

} // namespace react
} // namespace facebook

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
  return facebook::jni::initialize(vm, [] {
    facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
        &facebook::react::cxxModuleProvider;
    facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
        &facebook::react::javaModuleProvider;
    facebook::react::DefaultComponentsRegistry::
        registerComponentDescriptorsFromEntryPoint =
            &facebook::react::registerComponents;
  });
}

The important part within the OnLoad.cpp is this part in the javaModuleProvider-Function:

...
  auto module = <YourModuleNameHere>Spec_ModuleProvider(name, params);
  if(module != nullptr) {
    return module;
  }
...

That way your Module will be loaded. Keep in mind to update <YourModuleNameHere> to fit your module name (both in the import and within javaModuleProvider).