Native Modules とは?
- React Native 標準で用意されていない機能をネイティブで作成するための機構
- iOS:Objective-C(Swiftも可能)、Android:Java(Kotlinも可能)
- React Native における常套手段かつ最終手段
- UI がからむ場合は、Native UI Components を使用する
リソース
- React Native Modules
- https://facebook.github.io/react-native/docs/native-modules-ios
- https://facebook.github.io/react-native/docs/native-modules-android
- Native UI Components
- https://facebook.github.io/react-native/docs/native-components-ios
- https://facebook.github.io/react-native/docs/native-components-android
Native Modules で Native側から、OS名、OSバージョン名を取得する関数の実装を通して説明します。
デモのソース
JavaScript と ネイティブ間のデータの受け渡し
React Native Modules がよしなにマッピングしてくれる
型 | Objective-C | Java |
---|---|---|
string | NSString | String |
number | NSInteger,float,double,CGFloat,NSNumber | Integer,Double,Float |
boolean | BOOL,NSNumber | Boolean |
array | NSArray | ReadbleArray |
object | NSDictionary | ReadbleMap |
function | RCTResponseSenderBlock | Callback |
前提
- XCode, Android Studio インストール済み(React Nativeの環境構築で入る)
- react-native init NativeModulesTest
仕様
- モジュール名
- DeviceInformation
- 関数名
- getOSInfo
- 返り値
- 返り値1:OS名
- 返り値2:Version名
- 返り値例
- "iOS", "11.0"
- "Android", "8.1"
iOS
1分でわかる Objective-C
特徴
- C言語をベースに開発されたオブジェクト指向言語。
- Cの記法・ライブラリが全部動く。
- C言語にコンパイルされるので、激早。関数呼び出しはメッセージ呼び出し的になるのでネイティブCより少し遅い。
型
// ポインタを利用する。 文字列には、@ をつけて CStringと区別する。
NSString* string = @"hogehoge";
NSArray* array = @[@"hoge",@"fuga"];
NSDictionary* dictionary = @{@"key1":@"value1",@"key2":@"value2"};
クラス/関数の定義
- Hoge.h(宣言を書く)
@interface Person : NSObject
-(NSString*)nameWithType:(NSInteger)type;
@end
- Hoge.m(実装を書く)
@implementation
-(NSString*)nameWithType:(NSInteger)type {
return type == 0 @"Taro Suzuki" ? @"鈴木太郎";
}
@end
関数呼び出し
[person walk];
ブロック
- JavaScript でいうクロージャー。
- 厳密に型の定義がされる
[viewController presentViewController:childViewController animated:YES completion:^{
NSLog(@"completed");
}];
手順
① iOS用の プロジェクトを XCode で開く
② ググって OSVersion を取得する関数を調べる
③ DeviceInformation クラスの作成
④ DeviceInformation.h を編集
⑤ DeviceInformation.m を編集
⑥ JavaScript で 作成した Native Module を呼び出す
⑦ 実行
① iOS用の プロジェクトを XCode で開く
プロジェクトルート(NativeModulesTest) > ios > NativeModulesTest.xcodeproj
② ググって OSVersion を取得する関数を調べる
#import <UIKit/UIKit.h>
[UIDevice currentDevice].systemVersion
③ DeviceInformation クラスの作成
- NativeModuleTest ってなっているぐらいのところで右クリックして
New File
をクリック - Cocoa Touch Class を選択
- Class のところに
DeviceInformation
と入力して、右下Next
をクリック - ファイルを作成する場所を選んで(そのままでOK)、右下
Create
をクリック
④ DeviceInformation.h を編集
#import <Foundation/Foundation.h>
// 次の行を追加
#import <React/RCTBridgeModule.h>
// <RCTBridgeModule> を追加
@interface DeviceInformation : NSObject<RCTBridgeModule>
@end
⑤ DeviceInformation.m を編集
// 次の行を追加。UIDevice クラスを利用するのに必要
#import <UIKit/UIKit.h>
@implementation DeviceInformation
// JavaScript側にこのモジュール(DeviceInformation)を公開するよというマクロ。
// マクロとはC言語系の言語で使われるコンパイル時に置き換えて使用できる関数や定数のこと。
RCT_EXPORT_MODULE();
// JavaScript側に公開する関数の定義。getOSInfo(最初の:まで) が公開される関数名となる。
RCT_EXPORT_METHOD(getOSInfo:(RCTResponseSenderBlock)callback) {
// callbackに返り値となる値を配列で渡す
callback(@[@"iOS", [UIDevice currentDevice].systemVersion]);
}
@end
⑥ JavaScript で 作成した Native Module を呼び出す
// 中略
import {NativeModules} from 'react-native';
// 中略
export default class App extends Component<Props> {
constructor(props:Props) {
super(props);
this.state = {osInfo:''};
// 定義したモジュール名が、NativeModules に存在するようになる。
var DeviceInformation = NativeModules.DeviceInformation;
// 定義した関数名で呼び出し。ネイティブ上で callback に渡した引数の値がその順序で引数として渡される。
DeviceInformation.getOSInfo((os, version) => {
this.setState({osInfo: `${os} ${version}`});
});
}
// 中略
render() {
return (
<View style={styles.container}>
<Text>OS:{this.state.osInfo}</Text>
// 中略
⑦ 実行
- XCode 上で Product > Run (Cmd+R)
or
- react-native run-ios
Android
手順
① Android用の Project を Android Studio で開く
② ググって OSVersion を取得する関数を調べる
③ DeviceInformationModule クラスの作成
④ DeviceInformationPackage クラスの作成
⑤ MainApplicationを修正
⑥ JavaScriptの実装(iOSの実装で作成済み)
⑦ 実行
① Android用の Project を Android Studio で開く
- Android Studioを起動
- Open an Existing Android Studio project を選ぶ
- プロジェクトルート(NativeModulesTest) > android ディレクトリを選ぶ
- Gradleの設定で、Update してね的なメッセージがでるので迷わずupdate.
② ググって OSVersion を取得する関数を調べる
import android.os.Build;
Build.VERSION.RELEASE
③ DeviceInformationModule クラスの作成
- app > java > com.nativemoduletest ってなっているぐらいのところで右クリックして
New
-> 'Java Class' をクリック - Nameのところに
DeviceInformation
、Superclass にReactContextBaseJavaModule
と入力してOK
をクリック - Class のところに と入力して、右下
Next
をクリック - ファイルを作成する場所を選んで(そのままでOK)、右下
Create
をクリック
package com.nativemodulestest;
// 次のimportを追加
import android.os.Build;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
// ReactContextBaseJavaModule を継承する
public class DeviceInformationModule extends ReactContextBaseJavaModule{
// コンストラクタの定義。super(reactContext)するだけでよい。
public DeviceInformationModule(ReactApplicationContext reactContext) {
super(reactContext);
}
// getName() を override.この関数の返り値が、JavaScript に公開されるModule名になる。iOSとそろえる。
@Override
public String getName() {
return "DeviceInformation";
}
// @ReactMethod というアノテーションを行うことで、JavaScript側に定義したメソッドを公開すること示す。
@ReactMethod
public void getOSInfo(Callback callback) {
// Androidでは、JavaScriptへの返り値を 可変長引数で渡す。
callback.invoke("android",Build.VERSION.RELEASE);
}
④ DeviceInformationPackage クラスの作成
DeviceInformationModule と同様に、DeviceInformationPackageクラスを作成
package com.nativemodulestest;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DeviceInformationPackage implements ReactPackage {
// テンプレート的な実装。単に作成したDeviceInformationModule new して modules に追加
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DeviceInformationModule(reactContext));
return modules;
}
// View を使わない場合は、emptyList を返す
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
⑤ MainApplicationを修正
// 中略
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
// 直前の , と 次の行を追加
new DeviceInformationPackage()
);
}
⑥ JavaScriptの実装
iOSで実装済み
⑦ 実行
- Android Studio上で Run > Run 'app' (Ctrl+R)
or
- react-native run-android
まとめ
だいたいおまじない。実質的に実装したのは次の部分のみ。
- iOS
RCT_EXPORT_METHOD(getOSInfo:(RCTResponseSenderBlock)callback) {
callback(@[@"iOS", [UIDevice currentDevice].systemVersion]);
}
- Android
@ReactMethod
public void getOSInfo(Callback callback) {
callback.invoke("android",Build.VERSION.RELEASE);
}