iOSネイティブモジュール
ネイティブモジュールとネイティブコンポーネントは、レガシーアーキテクチャで使用されている安定した技術です。これらは、新しいアーキテクチャが安定した将来に非推奨となる予定です。新しいアーキテクチャでは、同様の結果を得るためにTurbo Native ModuleとFabric Native Componentを使用します。
iOS向けネイティブモジュールへようこそ。まず、ネイティブモジュールとは何かを紹介するネイティブモジュールのイントロダクションをお読みください。
カレンダーネイティブモジュールの作成
以下のガイドでは、JavaScriptからAppleのカレンダーAPIにアクセスできるようにするネイティブモジュールCalendarModule
を作成します。最終的には、JavaScriptからCalendarModule.createCalendarEvent('Dinner Party', 'My House');
を呼び出し、カレンダーイベントを作成するネイティブメソッドを起動できるようになります。
セットアップ
まず、React Nativeアプリケーション内のiOSプロジェクトをXcodeで開きます。React Nativeアプリ内のiOSプロジェクトは以下の場所にあります。

ネイティブコードの記述にはXcodeの使用を推奨します。XcodeはiOS開発向けに作られており、コードの構文エラーのような小さなエラーを迅速に解決するのに役立ちます。
カスタムネイティブモジュールファイルの作成
最初のステップは、主要なカスタムネイティブモジュールのヘッダーファイルと実装ファイルを作成することです。RCTCalendarModule.h
という新しいファイルを作成します。

そして、以下の内容を追加してください。
// RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end
作成するネイティブモジュールに合った任意の名前を使用できます。カレンダーネイティブモジュールを作成しているので、クラス名をRCTCalendarModule
とします。ObjCにはJavaやC++のような言語レベルの名前空間のサポートがないため、慣習としてクラス名の前にサブストリングを付けます。これは、アプリケーション名やインフラ名の略称などが考えられます。この例のRCTはReactを指します。
以下でわかるように、CalendarModuleクラスはRCTBridgeModule
プロトコルを実装しています。ネイティブモジュールは、RCTBridgeModule
プロトコルを実装したObjective-Cクラスです。
次に、ネイティブモジュールの実装を始めましょう。同じフォルダに、Xcodeのcocoa touch classを使用して対応する実装ファイルRCTCalendarModule.m
を作成し、以下の内容を含めます。
// RCTCalendarModule.m
#import "RCTCalendarModule.h"
@implementation RCTCalendarModule
// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE();
@end
モジュール名
今のところ、RCTCalendarModule.m
ネイティブモジュールにはRCT_EXPORT_MODULE
マクロのみが含まれています。これはネイティブモジュールクラスをReact Nativeにエクスポートし、登録します。RCT_EXPORT_MODULE
マクロは、JavaScriptコード内でモジュールがアクセス可能になる名前を指定するオプションの引数を取ることもできます。
この引数は文字列リテラルではありません。以下の例では、RCT_EXPORT_MODULE("CalendarModuleFoo")
ではなくRCT_EXPORT_MODULE(CalendarModuleFoo)
が渡されています。
// To export a module named CalendarModuleFoo
RCT_EXPORT_MODULE(CalendarModuleFoo);
すると、このネイティブモジュールはJSで次のようにアクセスできます。
const {CalendarModuleFoo} = ReactNative.NativeModules;
名前を指定しない場合、JavaScriptモジュール名はObjective-Cのクラス名から "RCT" または "RK" のプレフィックスを取り除いたものと一致します。
以下の例に従い、引数なしでRCT_EXPORT_MODULE
を呼び出してみましょう。その結果、モジュールはObjective-Cのクラス名からRCTを取り除いたCalendarModule
という名前でReact Nativeに公開されます。
// Without passing in a name this will export the native module name as the Objective-C class name with “RCT” removed
RCT_EXPORT_MODULE();
すると、このネイティブモジュールはJSで次のようにアクセスできます。
const {CalendarModule} = ReactNative.NativeModules;
ネイティブメソッドをJavaScriptにエクスポートする
React Nativeは、明示的に指定されない限り、ネイティブモジュール内のメソッドをJavaScriptに公開しません。これはRCT_EXPORT_METHOD
マクロを使用して行います。RCT_EXPORT_METHOD
マクロで書かれたメソッドは非同期であり、戻り値の型は常にvoidです。RCT_EXPORT_METHOD
メソッドからJavaScriptに結果を渡すためには、コールバックを使用するか、イベントを発行します(後述)。それでは、RCT_EXPORT_METHOD
マクロを使用してCalendarModule
ネイティブモジュール用のネイティブメソッドを設定しましょう。これをcreateCalendarEvent()
と名付け、今のところはnameとlocationを文字列として引数に取ります。引数の型のオプションについては後ほど説明します。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}
メソッドがRCTの引数変換に依存している場合(下記の引数の型を参照)を除き、TurboModulesでは
RCT_EXPORT_METHOD
マクロは不要になることに注意してください。最終的に、React NativeはRCT_EXPORT_MACRO
を削除する予定なので、RCTConvert
の使用は推奨しません。代わりに、メソッド本体内で引数変換を行うことができます。
createCalendarEvent()
メソッドの機能を構築する前に、メソッドにコンソールログを追加して、React NativeアプリケーションのJavaScriptから呼び出されたことを確認できるようにしましょう。ReactのRCTLog
APIを使用します。ファイルの先頭でそのヘッダーをインポートし、ログ呼び出しを追加します。
#import <React/RCTLog.h>
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
同期メソッド
同期的なネイティブメソッドを作成するには、RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD
を使用できます。
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getName)
{
return [[UIDevice currentDevice] name];
}
このメソッドの戻り値の型はオブジェクト型(id)でなければならず、JSONにシリアライズ可能であるべきです。これは、このフックがnilまたはJSON値(例: NSNumber, NSString, NSArray, NSDictionary)のみを返すことができることを意味します。
現時点では、同期メソッドの使用は推奨していません。メソッドを同期的に呼び出すと、パフォーマンスに大きなペナルティが生じ、ネイティブモジュールにスレッド関連のバグを引き起こす可能性があるためです。さらに、RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD
を使用する場合、アプリはGoogle Chromeデバッガーを使用できなくなります。これは、同期メソッドがJS VMとアプリでメモリを共有する必要があるためです。Google Chromeデバッガーの場合、React NativeはGoogle Chrome内のJS VMで実行され、WebSocketを介してモバイルデバイスと非同期に通信します。
作成したものをテストする
この時点で、iOSでのネイティブモジュールの基本的な骨組みができました。ネイティブモジュールにアクセスし、エクスポートされたメソッドをJavaScriptで呼び出してテストしてみましょう。
アプリケーション内でネイティブモジュールのcreateCalendarEvent()
メソッドへの呼び出しを追加したい場所を見つけます。以下は、アプリに追加できるコンポーネントNewModuleButton
の例です。NewModuleButton
のonPress()
関数内でネイティブモジュールを呼び出すことができます。
import React from 'react';
import {Button} from 'react-native';
const NewModuleButton = () => {
const onPress = () => {
console.log('We will invoke the native module here!');
};
return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};
export default NewModuleButton;
JavaScriptからネイティブモジュールにアクセスするには、まずReact NativeからNativeModules
をインポートする必要があります。
import {NativeModules} from 'react-native';
その後、NativeModules
からCalendarModule
ネイティブモジュールにアクセスできます。
const {CalendarModule} = NativeModules;
これでCalendarModuleネイティブモジュールが利用可能になったので、ネイティブメソッドcreateCalendarEvent()
を呼び出すことができます。以下では、NewModuleButton
のonPress()
メソッドに追加しています。
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};
最後のステップは、React Nativeアプリを再ビルドして、最新のネイティブコード(新しいネイティブモジュールを含む!)を利用できるようにすることです。React Nativeアプリケーションがあるコマンドラインで、以下を実行します。
- npm
- Yarn
npm run ios
yarn ios
イテレーション中のビルド
これらのガイドを進め、ネイティブモジュールを繰り返し開発する際には、JavaScriptから最新の変更にアクセスするためにアプリケーションのネイティブビルドを行う必要があります。これは、書いているコードがアプリケーションのネイティブ部分に存在するためです。React NativeのMetroバンドラーはJavaScriptの変更を監視し、JSバンドルを即座に再ビルドできますが、ネイティブコードに対してはそうではありません。したがって、最新のネイティブの変更をテストしたい場合は、上記のコマンドを使用して再ビルドする必要があります。
まとめ✨
これで、JavaScriptでネイティブモジュールのcreateCalendarEvent()
メソッドを呼び出せるはずです。関数内でRCTLog
を使用しているため、アプリでデバッグモードを有効にし、ChromeのJSコンソールまたはモバイルアプリデバッガーFlipperを見ることで、ネイティブメソッドが呼び出されていることを確認できます。ネイティブモジュールメソッドを呼び出すたびに、RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
のメッセージが表示されるはずです。

この時点で、iOSネイティブモジュールを作成し、React NativeアプリケーションのJavaScriptからそのメソッドを呼び出しました。ネイティブモジュールメソッドが受け取る引数の型や、ネイティブモジュール内でコールバックやPromiseを設定する方法など、さらに詳しく学ぶために読み進めてください。
カレンダーネイティブモジュールを超えて
より良いネイティブモジュールのエクスポート
上記のようにNativeModules
からネイティブモジュールを取得してインポートするのは、少し扱いにくいです。
ネイティブモジュールの利用者が、アクセスするたびにそのようなことをする必要がないように、モジュールのJavaScriptラッパーを作成することができます。`NativeCalendarModule.js`という名前の新しいJavaScriptファイルを作成し、以下の内容を記述します。
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;
このJavaScriptファイルは、JavaScript側の機能を追加するのにも良い場所になります。例えば、TypeScriptのような型システムを使用している場合、ここでネイティブモジュールの型アノテーションを追加できます。React NativeはまだネイティブからJSへの型安全性をサポートしていませんが、これらの型アノテーションにより、すべてのJSコードが型安全になります。これらのアノテーションは、将来的に型安全なネイティブモジュールに移行するのを容易にします。以下は、Calendar Moduleに型安全性を追加する例です。
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
*
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;
他のJavaScriptファイルでは、このようにしてネイティブモジュールにアクセスし、そのメソッドを呼び出すことができます。
import NativeCalendarModule from './NativeCalendarModule';
NativeCalendarModule.createCalendarEvent('foo', 'bar');
これは、
CalendarModule
をインポートする場所がNativeCalendarModule.js
と同じ階層にあることを前提としています。必要に応じて相対インポートを更新してください。
引数の型
ネイティブモジュールのメソッドがJavaScriptで呼び出されると、React Nativeは引数をJSオブジェクトから対応するObjective-C/Swiftオブジェクトに変換します。例えば、Objective-CのネイティブモジュールメソッドがNSNumberを受け入れる場合、JSでは数値を渡してメソッドを呼び出す必要があります。React Nativeが変換を処理します。以下は、ネイティブモジュールメソッドでサポートされている引数の型と、それらがマッピングされるJavaScriptの同等物のリストです。
Objective-C | JavaScript |
---|---|
NSString | string, ?string |
BOOL | boolean |
double | number |
NSNumber | ?number |
NSArray | Array, ?Array |
NSDictionary | Object, ?Object |
RCTResponseSenderBlock | Function (success) |
RCTResponseSenderBlock, RCTResponseErrorBlock | Function (failure) |
RCTPromiseResolveBlock, RCTPromiseRejectBlock | Promise |
以下の型は現在サポートされていますが、TurboModulesではサポートされません。使用を避けてください。
- Function (failure) -> RCTResponseErrorBlock
- Number -> NSInteger
- Number -> CGFloat
- Number -> float
iOSの場合、RCTConvert
クラスでサポートされている任意の引数型を持つネイティブモジュールメソッドを書くこともできます(サポートされている内容の詳細はRCTConvertを参照してください)。RCTConvertヘルパー関数はすべて、JSON値を入力として受け取り、それをネイティブのObjective-C型またはクラスにマッピングします。
定数のエクスポート
ネイティブモジュールは、ネイティブメソッドconstantsToExport()
をオーバーライドすることで定数をエクスポートできます。以下ではconstantsToExport()
がオーバーライドされ、JavaScriptで次のようにアクセスできるデフォルトのイベント名プロパティを含むDictionaryを返します。
- (NSDictionary *)constantsToExport
{
return @{ @"DEFAULT_EVENT_NAME": @"New Event" };
}
この定数は、JSでネイティブモジュールのgetConstants()
を呼び出すことでアクセスできます。
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);
技術的には、constantsToExport()
でエクスポートされた定数にNativeModule
オブジェクトから直接アクセスすることが可能です。これはTurboModulesではサポートされなくなるため、将来の移行を避けるために上記のアプローチに切り替えることをコミュニティに推奨します。
定数は初期化時にのみエクスポートされるため、実行時に
constantsToExport()
の値を変更してもJavaScript環境には影響しませんので注意してください。
iOSの場合、constantsToExport()
をオーバーライドするなら、+ requiresMainQueueSetup
も実装して、モジュールがJavaScriptコードの実行前にメインスレッドで初期化する必要があるかどうかをReact Nativeに知らせるべきです。そうしないと、+ requiresMainQueueSetup:
で明示的にオプトアウトしない限り、将来モジュールがバックグラウンドスレッドで初期化される可能性があるという警告が表示されます。モジュールがUIKitへのアクセスを必要としない場合は、+ requiresMainQueueSetup
にNOで応答するべきです。
コールバック
ネイティブモジュールは、コールバックというユニークな種類の引数もサポートしています。コールバックは、非同期メソッドのためにObjective-CからJavaScriptへデータを渡すために使用されます。また、ネイティブ側から非同期にJSを実行するためにも使用できます。
iOSでは、コールバックはRCTResponseSenderBlock
型を使用して実装されます。以下では、createCalendarEventMethod()
にコールバックパラメータmyCallBack
が追加されています。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
myCallback:(RCTResponseSenderBlock)callback)
その後、ネイティブ関数内でコールバックを呼び出し、JavaScriptに渡したい結果を配列で提供できます。RCTResponseSenderBlock
は、JavaScriptコールバックに渡すパラメータの配列という1つの引数のみを受け入れることに注意してください。以下では、以前の呼び出しで作成されたイベントのIDを返します。
コールバックはネイティブ関数が完了した直後に呼び出されるわけではないことを強調しておくことが重要です。通信は非同期であることを覚えておいてください。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSInteger eventId = ...
callback(@[@(eventId)]);
RCTLogInfo(@"Pretending to create an event %@ at %@", title, location);
}
このメソッドは、JavaScriptで以下のようにアクセスできます。
const onSubmit = () => {
CalendarModule.createCalendarEvent(
'Party',
'04-12-2020',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};
ネイティブモジュールは、コールバックを一度だけ呼び出すことになっています。しかし、コールバックを保存しておき、後で呼び出すことも可能です。このパターンは、デリゲートを必要とするiOS APIをラップするためによく使用されます。例としてRCTAlertManager
を参照してください。コールバックが一度も呼び出されない場合、メモリリークが発生します。
コールバックでのエラーハンドリングには2つのアプローチがあります。1つ目はNodeの慣習に従い、コールバック配列に渡される最初の引数をエラーオブジェクトとして扱うことです。
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSNumber *eventId = [NSNumber numberWithInt:123];
callback(@[[NSNull null], eventId]);
}
JavaScriptでは、最初の引数をチェックしてエラーが渡されたかどうかを確認できます。
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};
もう一つの選択肢は、onFailureとonSuccessという2つの別々のコールバックを使用することです。
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title
location:(NSString *)location
errorCallback: (RCTResponseSenderBlock)errorCallback
successCallback: (RCTResponseSenderBlock)successCallback)
{
@try {
NSNumber *eventId = [NSNumber numberWithInt:123];
successCallback(@[eventId]);
}
@catch ( NSException *e ) {
errorCallback(@[e]);
}
}
そしてJavaScriptでは、エラーと成功のレスポンスに対して別々のコールバックを追加できます。
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};
JavaScriptにエラーのようなオブジェクトを渡したい場合は、RCTUtils.h
のRCTMakeError
を使用してください。現時点では、これはErrorの形をした辞書をJavaScriptに渡すだけですが、React Nativeは将来的に本物のJavaScript Errorオブジェクトを自動生成することを目指しています。また、エラーコールバックに使用されるRCTResponseErrorBlock
引数を提供することもできます。これはNSError *
オブジェクトを受け入れます。この引数型はTurboModulesではサポートされないことに注意してください。
Promise
ネイティブモジュールはPromiseを解決することもでき、これによりJavaScript、特にES2016のasync/await
構文を使用する際のコードが簡潔になります。ネイティブモジュールメソッドの最後のパラメータがRCTPromiseResolveBlock
とRCTPromiseRejectBlock
である場合、対応するJSメソッドはJS Promiseオブジェクトを返します。
上記のコードをコールバックの代わりにPromiseを使用するようにリファクタリングすると、このようになります。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSInteger eventId = createCalendarEvent();
if (eventId) {
resolve(@(eventId));
} else {
reject(@"event_failure", @"no event id returned", nil);
}
}
このメソッドのJavaScript側はPromiseを返します。これは、async関数内でawait
キーワードを使用してそれを呼び出し、その結果を待つことができることを意味します。
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'my house',
);
console.log(`Created a new event with id ${eventId}`);
} catch (e) {
console.error(e);
}
};
JavaScriptへのイベント送信
ネイティブモジュールは、直接呼び出されることなくJavaScriptにイベントを通知できます。たとえば、ネイティブのiOSカレンダーアプリからのカレンダーイベントが間もなく発生することをJavaScriptに通知したい場合があります。これを行うための推奨される方法は、RCTEventEmitter
をサブクラス化し、supportedEvents
を実装し、self sendEventWithName
を呼び出すことです。
ヘッダークラスを更新してRCTEventEmitter
をインポートし、RCTEventEmitter
をサブクラス化します。
// CalendarModule.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>
@end
JavaScriptコードは、モジュールの周りに新しいNativeEventEmitter
インスタンスを作成することで、これらのイベントを購読できます。
リスナーがいないときにイベントを発行すると、リソースを不必要に消費しているという警告が表示されます。これを避け、モジュールのワークロードを最適化するため(例えば、上流の通知から購読を解除したり、バックグラウンドタスクを一時停止したりする)、RCTEventEmitter
サブクラスでstartObserving
とstopObserving
をオーバーライドできます。
@implementation CalendarModule
{
bool hasListeners;
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[@"name"];
if (hasListeners) {// Only send events if anyone is listening
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}
}
スレッド
ネイティブモジュールが独自のメソッドキューを提供しない限り、どのスレッドで呼び出されているかについて何の仮定もすべきではありません。現在、ネイティブモジュールがメソッドキューを提供しない場合、React Nativeはそれに対して別のGCDキューを作成し、そこでそのメソッドを呼び出します。これは実装の詳細であり、変更される可能性があることに注意してください。ネイティブモジュールに明示的にメソッドキューを提供したい場合は、ネイティブモジュールで(dispatch_queue_t) methodQueue
メソッドをオーバーライドします。例えば、メインスレッドのみのiOS APIを使用する必要がある場合は、次のように指定すべきです。
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
同様に、操作に時間がかかる可能性がある場合、ネイティブモジュールは操作を実行するための独自のキューを指定できます。繰り返しになりますが、現在React Nativeはネイティブモジュールに別のメソッドキューを提供しますが、これは依存すべきではない実装の詳細です。独自のメソッドキューを提供しない場合、将来的には、ネイティブモジュールの長時間実行される操作が、他の無関係なネイティブモジュールで実行されている非同期呼び出しをブロックする可能性があります。例えば、ここのRCTAsyncLocalStorage
モジュールは、Reactキューが潜在的に遅いディスクアクセスを待ってブロックされないように、独自のキューを作成します。
- (dispatch_queue_t)methodQueue
{
return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}
指定されたmethodQueue
は、モジュール内のすべてのメソッドで共有されます。メソッドの1つだけが長時間実行される(または何らかの理由で他のメソッドとは異なるキューで実行する必要がある)場合は、メソッド内でdispatch_async
を使用して、その特定のメソッドのコードを他のキューで実行し、他のメソッドに影響を与えないようにすることができます。
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Call long-running code on background thread
...
// You can invoke callback from any thread/queue
callback(@[...]);
});
}
モジュール間でのディスパッチキューの共有
methodQueue
メソッドはモジュールが初期化される際に一度だけ呼ばれ、その後React Nativeによって保持されます。そのため、モジュール内でそれを利用したい場合を除き、自分でキューへの参照を保持する必要はありません。しかし、複数のモジュール間で同じキューを共有したい場合は、それぞれに対して同じキューインスタンスを保持し、返すようにする必要があります。
依存性の注入
React Nativeは、登録されているすべてのネイティブモジュールを自動的に作成し、初期化します。しかし、例えば依存性を注入するために、独自のモジュールインスタンスを作成し、初期化したい場合があるかもしれません。
これは、`RCTBridgeDelegate`プロトコルを実装するクラスを作成し、そのデリゲートを引数として`RCTBridge`を初期化し、初期化されたブリッジで`RCTRootView`を初期化することによって行うことができます。
id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];
RCTRootView *rootView = [[RCTRootView alloc]
initWithBridge:bridge
moduleName:kModuleName
initialProperties:nil];
Swiftのエクスポート
Swiftにはマクロのサポートがないため、React Native内でネイティブモジュールとそのメソッドをJavaScriptに公開するには、もう少し設定が必要です。しかし、動作は比較的同じです。同じCalendarModule
をSwiftクラスとして持っているとしましょう。
// CalendarModule.swift
@objc(CalendarModule)
class CalendarModule: NSObject {
@objc(addEvent:location:date:)
func addEvent(_ name: String, location: String, date: NSNumber) -> Void {
// Date is ready to use!
}
@objc
func constantsToExport() -> [String: Any]! {
return ["someKey": "someValue"]
}
}
クラスと関数がObjective-Cランタイムに正しくエクスポートされるように、
@objc
修飾子を使用することが重要です。
次に、必要な情報をReact Nativeに登録するプライベートな実装ファイルを作成します。
// CalendarModuleBridge.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)
RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)
@end
SwiftとObjective-Cに慣れていない方のために説明すると、iOSプロジェクトでこれら2つの言語を混在させる場合、Objective-CファイルをSwiftに公開するために、ブリッジングヘッダーとして知られる追加のブリッジングファイルも必要になります。Xcodeの`File > New File`メニューオプションからSwiftファイルをアプリに追加すると、Xcodeがこのヘッダーファイルの作成を提案してくれます。このヘッダーファイルに`RCTBridgeModule.h`をインポートする必要があります。
// CalendarModule-Bridging-Header.h
#import <React/RCTBridgeModule.h>
また、RCT_EXTERN_REMAP_MODULE
と_RCT_EXTERN_REMAP_METHOD
を使用して、エクスポートするモジュールやメソッドのJavaScript名を変更することもできます。詳細については、RCTBridgeModule
を参照してください。
サードパーティモジュールを作成する際の重要事項: Swiftを含む静的ライブラリはXcode 9以降でのみサポートされています。モジュールに含まれるiOS静的ライブラリでSwiftを使用する場合にXcodeプロジェクトがビルドされるためには、メインのアプリプロジェクト自体にSwiftコードとブリッジングヘッダーが含まれている必要があります。アプリプロジェクトにSwiftコードが含まれていない場合、回避策として空の.swiftファイルと空のブリッジングヘッダーを1つずつ用意する方法があります。
予約されたメソッド名
invalidate()
ネイティブモジュールは、iOS上で`invalidate()`メソッドを実装することでRCTInvalidatingプロトコルに準拠できます。このメソッドは、ネイティブブリッジが無効化されたとき(例:devmodeでのリロード時)に呼び出されることがあります。ネイティブモジュールに必要なクリーンアップを行うために、必要に応じてこのメカニズムを使用してください。