メインコンテンツへスキップ

iOSネイティブモジュール

情報

ネイティブモジュールとネイティブコンポーネントは、レガシーアーキテクチャで使用される安定したテクノロジーです。新しいアーキテクチャが安定した時点で、将来的に非推奨となる予定です。新しいアーキテクチャでは、同様の結果を得るために、TurboネイティブモジュールFabricネイティブコンポーネントを使用します。

iOS用ネイティブモジュールへようこそ。まずは、ネイティブモジュールとは何かについて説明したネイティブモジュールの紹介をご覧ください。

カレンダーネイティブモジュールの作成

次のガイドでは、JavaScriptからAppleのカレンダーAPIにアクセスできるようにするネイティブモジュールCalendarModuleを作成します。最後に、JavaScriptからCalendarModule.createCalendarEvent('Dinner Party', 'My House');を呼び出し、カレンダーイベントを作成するネイティブメソッドを呼び出せるようになります。

セットアップ

まず、XcodeでReact Nativeアプリケーション内のiOSプロジェクトを開きます。React Nativeアプリ内のiOSプロジェクトはここにあります。

Image of opening up an iOS project within a React Native app inside of xCode.
iOSプロジェクトがある場所の画像

ネイティブコードの記述にはXcodeを使用することをお勧めします。XcodeはiOS開発用に構築されており、これを使用すると、コード構文などの小さなエラーを迅速に解決できます。

カスタムネイティブモジュールファイルの作成

最初のステップは、メインのカスタムネイティブモジュールのヘッダーファイルと実装ファイルを作成することです。RCTCalendarModule.hという名前の新しいファイルを作成します。

Image of creating a class called  RCTCalendarModule.h.
AppDelegateと同じフォルダ内にカスタムネイティブモジュールファイルを作成する画像

そして、以下を追加します。

//  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クラスを使用して、同じフォルダに、対応する実装ファイル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モジュール名は、"RCT"または"RK"のプレフィックスが削除されたObjective-Cクラス名と一致します。

以下の例に従って、引数なしでRCT_EXPORT_MODULEを呼び出しましょう。その結果、モジュールは、RCTが削除されたObjective-Cクラス名である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()と呼び、今のところ、名前と場所の引数を文字列として受け取るようにします。引数の型のオプションはすぐに説明します。

RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}

RCT_EXPORT_METHODマクロは、メソッドがRCT引数変換(以下の引数の型を参照)に依存しない限り、TurboModulesでは必要なくなることに注意してください。最終的に、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内で実行され、WebSockets経由でモバイルデバイスと非同期的に通信します。

構築したものをテストする

この時点で、iOSでネイティブモジュールの基本的な足場を設定しました。JavaScriptでネイティブモジュールにアクセスし、エクスポートされたメソッドを呼び出してテストします。

アプリケーション内のネイティブモジュールのcreateCalendarEvent()メソッドへの呼び出しを追加する場所を見つけます。以下は、アプリに追加できるコンポーネントNewModuleButtonの例です。NewModuleButtononPress()関数内でネイティブモジュールを呼び出すことができます。

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()を呼び出すことができます。以下では、NewModuleButtononPress()メソッドに追加されています。

const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};

最後のステップは、React Nativeアプリをリビルドして、最新のネイティブコード(新しいネイティブモジュールを含む)を使用できるようにすることです。React Nativeアプリケーションがあるコマンドラインで、以下を実行します。

npm run ios

反復しながらビルドする

これらのガイドを進め、ネイティブモジュールを反復処理する場合、JavaScriptから最新の変更にアクセスするには、アプリケーションのネイティブリビルドを実行する必要があります。これは、記述しているコードがアプリケーションのネイティブ部分内にあるためです。React Nativeのmetroバンドラーは、JavaScriptの変更を監視し、JSバンドルをその場でリビルドできますが、ネイティブコードではそうではありません。したがって、最新のネイティブ変更をテストする場合は、上記のコマンドを使用してリビルドする必要があります。

まとめ✨

JavaScriptでネイティブモジュールのcreateCalendarEvent()メソッドを呼び出せるようになっているはずです。関数でRCTLogを使用しているため、アプリでデバッグモードを有効にすると、ChromeまたはモバイルアプリデバッガーFlipperのJSコンソールを確認することで、ネイティブメソッドが呼び出されていることを確認できます。ネイティブモジュールメソッドを呼び出すたびに、RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);メッセージが表示されるはずです。

Image of logs.
FlipperでのiOSログの画像

この時点で、iOSネイティブモジュールを作成し、React NativeアプリケーションのJavaScriptからそのメソッドを呼び出しました。ネイティブモジュールメソッドが受け取る引数の型や、ネイティブモジュール内でコールバックとプロミスを設定する方法などについて詳しく学ぶことができます。

カレンダーネイティブモジュールを超えて

より良いネイティブモジュールエクスポート

上記のように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はまだNativeからJSへの型安全性をサポートしていませんが、これらの型注釈を使用すると、すべてのJSコードが型安全になります。これらの注釈は、将来的には型安全なネイティブモジュールに切り替えることも容易にします。以下は、カレンダーモジュールに型安全性を追加する例です。

/**
* 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ではnumberを使用してメソッドを呼び出す必要があります。React Nativeが変換を処理します。以下は、ネイティブモジュールメソッドでサポートされている引数の型と、それらがマッピングされるJavaScriptの同等の型の一覧です。

Objective-CJavaScript
NSStringstring, ?string
BOOLboolean
doublenumber
NSNumber?number
NSArrayArray, ?Array
NSDictionaryObject, ?Object
RCTResponseSenderBlockFunction (成功)
RCTResponseSenderBlock, RCTResponseErrorBlockFunction (失敗)
RCTPromiseResolveBlock, RCTPromiseRejectBlockPromise

次の型は現在サポートされていますが、TurboModulesではサポートされません。使用を避けてください。

  • Function (失敗) -> RCTResponseErrorBlock
  • Number -> NSInteger
  • Number -> CGFloat
  • Number -> float

iOSの場合、RCTConvertクラスでサポートされている任意の引数型を使用してネイティブモジュールメソッドを作成することもできます(サポートされている内容の詳細については、RCTConvertを参照してください)。RCTConvertヘルパー関数はすべて、JSON値を入力として受け取り、ネイティブのObjective-C型またはクラスにマッピングします。

定数のエクスポート

ネイティブモジュールは、ネイティブメソッドconstantsToExport()をオーバーライドすることにより、定数をエクスポートできます。以下では、constantsToExport()がオーバーライドされ、JavaScriptで次のようにアクセスできるデフォルトのイベント名プロパティを含む辞書を返します。

- (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も実装して、React NativeにJavaScriptコードが実行される前に、メインスレッドでモジュールを初期化する必要があるかどうかを知らせる必要があります。そうしないと、将来、+ requiresMainQueueSetup:で明示的にオプトアウトしない限り、モジュールがバックグラウンドスレッドで初期化される可能性があるという警告が表示されます。モジュールがUIKitへのアクセスを必要としない場合は、+ requiresMainQueueSetupにNOで応答する必要があります。

コールバック

ネイティブモジュールは、コールバックというユニークな種類の引数もサポートしています。コールバックは、非同期メソッドのためにObjective-CからJavaScriptにデータを渡すために使用されます。ネイティブ側からJSを非同期的に実行するためにも使用できます。

iOSの場合、コールバックは型RCTResponseSenderBlockを使用して実装されます。以下では、コールバックパラメータmyCallBackcreateCalendarEventMethod()に追加されています。

RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
myCallback:(RCTResponseSenderBlock)callback)

次に、ネイティブ関数でコールバックを呼び出し、JavaScriptに渡したい結果を配列で提供できます。RCTResponseSenderBlockは1つの引数、つまりJavaScriptコールバックに渡すパラメータの配列のみを受け入れることに注意してください。以下では、以前の呼び出しで作成されたイベントの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}`);
},
);
};

ネイティブモジュールは、コールバックを1回だけ呼び出すことになっています。ただし、コールバックを保存して後で呼び出すことは可能です。このパターンは、デリゲートを必要とする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.hRCTMakeErrorを使用してください。現在、これはError型の辞書をJavaScriptに渡すだけですが、React Nativeは将来、実際のJavaScript Errorオブジェクトを自動的に生成することを目指しています。NSError \* objectを受け入れるエラーコールバックに使用されるRCTResponseErrorBlock引数を指定することもできます。この引数型は、TurboModulesではサポートされないことに注意してください。

Promise

ネイティブモジュールは、特にES2016のasync/await構文を使用する場合に、JavaScriptを簡素化できるPromiseを返すこともできます。ネイティブモジュールメソッドの最後のパラメータがRCTPromiseResolveBlockRCTPromiseRejectBlockの場合、対応する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を実装し、sendEventWithNameを呼び出すことです。

ヘッダークラスを更新して、RCTEventEmitterをインポートし、RCTEventEmitterをサブクラス化します。

//  CalendarModule.h

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>
@end

JavaScriptコードは、モジュールを中心に新しいNativeEventEmitterインスタンスを作成することにより、これらのイベントをサブスクライブできます。

リスナーが存在しないときにイベントを送信して不必要にリソースを消費すると、警告が表示されます。これを回避し、モジュールのワークロードを最適化(例:アップストリーム通知のサブスクライブ解除やバックグラウンドタスクの一時停止)するために、RCTEventEmitterサブクラスでstartObservingstopObservingをオーバーライドできます。

@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のファイル>新規ファイルメニューオプションから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ファイルと空のブリッジヘッダーを使用できます。

予約済みのメソッド名

invalidate()

ネイティブモジュールは、invalidate()メソッドを実装することにより、iOSでRCTInvalidatingプロトコルに準拠できます。このメソッドは、ネイティブブリッジが無効化されたとき(開発モードのリロード時など)に呼び出すことができます。ネイティブモジュールの必要なクリーンアップを行うために、必要に応じてこのメカニズムを使用してください。