クロスプラットフォームネイティブモジュール (C++)
C++ でモジュールを作成することは、Android と iOS の間でプラットフォームに依存しないコードを共有する最良の方法です。純粋な C++ モジュールを使用すると、ロジックを一度書くだけで、プラットフォーム固有のコードを書く必要なく、すべてのプラットフォームからすぐに再利用できます。
このガイドでは、純粋な C++ Turbo Native Module の作成について説明します。
- JS 仕様の作成
- スカフォールディングを生成するための Codegen の設定
- ネイティブロジックの実装
- Android および iOS アプリケーションへのモジュールの登録
- JS での変更のテスト
このガイドの残りの部分では、コマンドを実行してアプリケーションを作成済みであることを前提としています。
npx @react-native-community/cli@latest init SampleApp --version 0.80.0
1. JS 仕様の作成
純粋な C++ Turbo Native Module は Turbo Native Module です。Codegen がスカフォールディングコードを作成できるように、仕様ファイル (spec ファイルとも呼ばれます) が必要です。仕様ファイルは、JS で Turbo Native Module にアクセスするためにも使用されます。
仕様ファイルは、型付き JS 方言で記述する必要があります。React Native は現在、Flow または TypeScript をサポートしています。
- アプリのルートフォルダ内に、
specsという名前の新しいフォルダを作成します。 - 次のコードで
NativeSampleModule.tsという名前の新しいファイルを作成します。
すべての Native Turbo Module の仕様ファイルには Native というプレフィックスが必要です。そうしないと、Codegen はそれらを無視します。
- TypeScript
- Flow
// @flow
import type {TurboModule} from 'react-native'
import { TurboModuleRegistry } from "react-native";
export interface Spec extends TurboModule {
+reverseString: (input: string) => string;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
"NativeSampleModule"
): Spec);
import {TurboModule, TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
readonly reverseString: (input: string) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeSampleModule',
);
2. Codegen の設定
次のステップは、package.json で Codegen を設定することです。ファイルに以下を含めるように更新します。
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpecs",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.sampleapp.specs"
}
},
"dependencies": {
この設定は、Codegen に specs フォルダ内の仕様ファイルを探すように指示します。また、Codegen に modules のコードのみを生成し、生成されたコードを AppSpecs として名前空間化するように指示します。
3. ネイティブコードの記述
C++ Turbo Native Module を記述すると、Android と iOS の間でコードを共有できます。したがって、コードは一度だけ記述し、C++ コードが取得されるようにプラットフォームに適用する必要がある変更点について説明します。
-
androidおよびiosフォルダと同じレベルにsharedという名前のフォルダを作成します。 -
sharedフォルダ内に、NativeSampleModule.hという名前の新しいファイルを作成します。shared/NativeSampleModule.h#pragma once
#include <AppSpecsJSI.h>
#include <memory>
#include <string>
namespace facebook::react {
class NativeSampleModule : public NativeSampleModuleCxxSpec<NativeSampleModule> {
public:
NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker);
std::string reverseString(jsi::Runtime& rt, std::string input);
};
} // namespace facebook::react -
sharedフォルダ内に、NativeSampleModule.cppという名前の新しいファイルを作成します。shared/NativeSampleModule.cpp#include "NativeSampleModule.h"
namespace facebook::react {
NativeSampleModule::NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker)
: NativeSampleModuleCxxSpec(std::move(jsInvoker)) {}
std::string NativeSampleModule::reverseString(jsi::Runtime& rt, std::string input) {
return std::string(input.rbegin(), input.rend());
}
} // namespace facebook::react
作成した2つのファイルを見てみましょう。
NativeSampleModule.hファイルは、純粋な C++ TurboModule のヘッダーファイルです。includeステートメントは、Codegen によって作成され、実装する必要があるインターフェースと基底クラスを含む仕様が含まれることを保証します。- モジュールは、その名前空間にあるすべての型にアクセスできるように、
facebook::react名前空間に存在します。 - クラス
NativeSampleModuleは実際の Turbo Native Module クラスであり、このクラスを Turbo Native Module として機能させるためのグルーコードとボイラープレートコードを含むNativeSampleModuleCxxSpecクラスを拡張します。 - 最後に、必要に応じて JS と通信するための
CallInvokerへのポインタを受け入れるコンストラクタと、実装する必要がある関数のプロトタイプがあります。
NativeSampleModule.cpp ファイルは、Turbo Native Module の実際の実装であり、仕様で宣言したコンストラクタとメソッドを実装します。
4. モジュールをプラットフォームに登録する
次のステップでは、モジュールをプラットフォームに登録します。これにより、ネイティブコードが JS に公開され、React Native アプリケーションが JS レイヤーからネイティブメソッドを呼び出すことができるようになります。
これは、プラットフォーム固有のコードを記述する必要がある唯一の時です。
Android
Android アプリが C++ Turbo Native Module を効果的にビルドできるようにするには、次の操作を行う必要があります。
- C++ コードにアクセスするための
CMakeLists.txtを作成します。 build.gradleを変更して、新しく作成したCMakeLists.txtファイルを指すようにします。- Android アプリに
OnLoad.cppファイルを作成して、新しい Turbo Native Module を登録します。
1. CMakeLists.txt ファイルを作成する
Android はビルドに CMake を使用します。CMake は、ビルドできるように共有フォルダで定義したファイルにアクセスする必要があります。
- 新しいフォルダ
SampleApp/android/app/src/main/jniを作成します。jniフォルダは、Android の C++ 側が存在する場所です。 CMakeLists.txtファイルを作成し、このコンテキストを追加します。
cmake_minimum_required(VERSION 3.13)
# Define the library name here.
project(appmodules)
# This file includes all the necessary to let you build your React Native application
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
# Define where the additional source code lives. We need to crawl back the jni, main, src, app, android folders
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ../../../../../shared/NativeSampleModule.cpp)
# Define where CMake can find the additional header files. We need to crawl back the jni, main, src, app, android folders
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ../../../../../shared)
CMake ファイルは次のことを行います。
- すべてのアプリ C++ コードが含まれる
appmodulesライブラリを定義します。 - ベースの React Native の CMake ファイルをロードします。
target_sourcesディレクティブを使用してビルドする必要があるモジュール C++ ソースコードを追加します。デフォルトでは、React Native はすでにappmodulesライブラリにデフォルトのソースを設定していますが、ここではカスタムのソースを含めます。C++ Turbo Module が存在するsharedフォルダにjniフォルダから戻る必要があることがわかります。- CMake がモジュールヘッダーファイルを見つけることができる場所を指定します。この場合も、
jniフォルダから戻る必要があります。
2. build.gradle を変更してカスタム C++ コードを含める
Gradle は Android ビルドをオーケストレーションするツールです。Turbo Native Module をビルドするために CMake ファイルを見つけることができる場所を Gradle に伝える必要があります。
SampleApp/android/app/build.gradleファイルを開きます。- 既存の
androidブロック内に次のブロックを Gradle ファイルに追加します。
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dokyumento.jp/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
+ externalNativeBuild {
+ cmake {
+ path "src/main/jni/CMakeLists.txt"
+ }
+ }
}
このブロックは、CMake ファイルを探す場所を Gradle ファイルに伝えます。パスは build.gradle ファイルが存在するフォルダからの相対パスなので、jni フォルダ内の CMakeLists.txt ファイルへのパスを追加する必要があります。
3. 新しい Turbo Native Module を登録する
最後のステップは、新しい C++ Turbo Native Module をランタイムに登録することです。これにより、JS が C++ Turbo Native Module を要求したときに、アプリはそれを見つける場所を知り、それを返すことができます。
- フォルダ
SampleApp/android/app/src/main/jniから、次のコマンドを実行します。
curl -O https://raw.githubusercontent.com/facebook/react-native/v0.76.0/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp
- 次に、このファイルを次のように変更します。
#include <DefaultComponentsRegistry.h>
#include <DefaultTurboModuleManagerDelegate.h>
#include <autolinking.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncore.h>
+ // Include the NativeSampleModule header
+ #include <NativeSampleModule.h>
//...
std::shared_ptr<TurboModule> cxxModuleProvider(
const std::string& name,
const std::shared_ptr<CallInvoker>& jsInvoker) {
// Here you can provide your CXX Turbo Modules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a module called `NativeCxxModuleExample`):
//
// if (name == NativeCxxModuleExample::kModuleName) {
// return std::make_shared<NativeCxxModuleExample>(jsInvoker);
// }
+ // This code register the module so that when the JS side asks for it, the app can return it
+ if (name == NativeSampleModule::kModuleName) {
+ return std::make_shared<NativeSampleModule>(jsInvoker);
+ }
// And we fallback to the CXX module providers autolinked
return autolinking_cxxModuleProvider(name, jsInvoker);
}
// leave the rest of the file
これらの手順は、React Native から元の OnLoad.cpp ファイルをダウンロードし、C++ Turbo Native Module をアプリにロードするために安全に上書きできるようにします。
ファイルをダウンロードしたら、次の方法で変更できます。
- モジュールを指すヘッダーファイルを含める
- JS がそれを要求したときにアプリがそれを返すことができるように Turbo Native Module を登録する。
これで、プロジェクトルートから yarn android を実行して、アプリが正常にビルドされるのを確認できます。
iOS
iOS アプリが C++ Turbo Native Module を効果的にビルドできるようにするには、次の操作を行う必要があります。
- Pods をインストールし、Codegen を実行します。
sharedフォルダを iOS プロジェクトに追加します。- アプリケーションに C++ Turbo Native Module を登録します。
1. Pods をインストールし、Codegen を実行する。
最初に実行する必要があるステップは、iOS アプリケーションを準備するたびに実行する通常のステップです。CocoaPods は、React Native の依存関係をセットアップおよびインストールするために使用するツールであり、そのプロセスの一部として、Codegen も実行します。
cd ios
bundle install
bundle exec pod install
2. 共有フォルダを iOS プロジェクトに追加する
このステップは、Xcode に表示されるように shared フォルダをプロジェクトに追加します。
- CocoaPods で生成された Xcode Workspace を開きます。
cd ios
open SampleApp.xcworkspace
- 左側の
SampleAppプロジェクトをクリックし、Add files to "Sample App"...を選択します。

sharedフォルダを選択し、Addをクリックします。

すべて正しく行っていれば、左側のプロジェクトは次のようになります。

3. アプリに Cxx Turbo Native Module を登録する
アプリに純粋な Cxx Turbo Native Module を登録するには、次の操作を行う必要があります。
- ネイティブモジュール用の
ModuleProviderを作成します。 - JS モジュール名を ModuleProvider クラスに関連付けるように
package.jsonを設定します。
ModuleProvider は、純粋な C++ モジュールと iOS アプリの残りの部分を接着する Objective-C++ です。
3.1 ModuleProvider の作成
- Xcode から、
SampleAppプロジェクトを選択し、⌘ + N を押して新しいファイルを作成します。 Cocoa Touch Classテンプレートを選択します。- 名前
SampleNativeModuleProviderを追加します (他のフィールドはSubclass of: NSObjectおよびLanguage: Objective-Cのままにします)。 - 「次へ」をクリックしてファイルを生成します。
SampleNativeModuleProvider.mをSampleNativeModuleProvider.mmに名前変更します。mm拡張子は Objective-C++ ファイルを示します。SampleNativeModuleProvider.hの内容を次のように実装します。
#import <Foundation/Foundation.h>
#import <ReactCommon/RCTTurboModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface NativeSampleModuleProvider : NSObject <RCTModuleProvider>
@end
NS_ASSUME_NONNULL_END
これは、RCTModuleProvider プロトコルに準拠する NativeSampleModuleProvider オブジェクトを宣言します。
SampleNativeModuleProvider.mmの内容を次のように実装します。
#import "NativeSampleModuleProvider.h"
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/TurboModule.h>
#import "NativeSampleModule.h"
@implementation NativeSampleModuleProvider
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeSampleModule>(params.jsInvoker);
}
@end
このコードは、getTurboModule: メソッドが呼び出されたときに純粋な C++ NativeSampleModule を作成することで RCTModuleProvider プロトコルを実装します。
3.2 package.json の更新
最後のステップは、Native Module の JS 仕様とネイティブコードでのそれらの仕様の具体的な実装との間のリンクを React Native に伝えるために package.json を更新することです。
package.json を次のように変更します。
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpecs",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.sampleapp.specs"
},
"ios": {
"modulesProvider": {
"NativeSampleModule": "NativeSampleModuleProvider"
}
}
},
"dependencies": {
この時点で、codegen が再度実行されて新しいファイルが生成されるように、pods を再インストールする必要があります。
# from the ios folder
bundle exec pod install
open SampleApp.xcworkspace
Xcode からアプリケーションをビルドすると、正常にビルドできるはずです。
5. コードのテスト
C++ Turbo Native Module を JS からアクセスする時が来ました。そのためには、App.tsx ファイルを変更して Turbo Native Module をインポートし、コード内でそれを呼び出す必要があります。
App.tsxファイルを開きます。- テンプレートの内容を次のコードに置き換えます。
import React from 'react';
import {
Button,
SafeAreaView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import SampleTurboModule from './specs/NativeSampleModule';
function App(): React.JSX.Element {
const [value, setValue] = React.useState('');
const [reversedValue, setReversedValue] = React.useState('');
const onPress = () => {
const revString = SampleTurboModule.reverseString(value);
setReversedValue(revString);
};
return (
<SafeAreaView style={styles.container}>
<View>
<Text style={styles.title}>
Welcome to C++ Turbo Native Module Example
</Text>
<Text>Write down here he text you want to revert</Text>
<TextInput
style={styles.textInput}
placeholder="Write your text here"
onChangeText={setValue}
value={value}
/>
<Button title="Reverse" onPress={onPress} />
<Text>Reversed text: {reversedValue}</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 18,
marginBottom: 20,
},
textInput: {
borderColor: 'black',
borderWidth: 1,
borderRadius: 5,
padding: 10,
marginTop: 10,
},
});
export default App;
このアプリの興味深い行は次のとおりです。
import SampleTurboModule from './specs/NativeSampleModule';: この行はアプリに Turbo Native Module をインポートします。onPressコールバック内のconst revString = SampleTurboModule.reverseString(value);: これはアプリで Turbo Native Module を使用する方法です。
この例のため、そして可能な限り短くするために、仕様ファイルをアプリに直接インポートしました。この場合のベストプラクティスは、仕様をラップする別のファイルを作成し、そのファイルをアプリケーションで使用することです。これにより、仕様の入力を準備でき、JS でそれらをより細かく制御できます。
おめでとうございます。最初の C++ Turbo Native Module を記述しました!
![]() | ![]() |

