はじめに
iOSのネイティブAPIにアクセスするために、ReactNatveにはNative Module
というAPIが用意されています。
今回は、Swift
でネイティブモジュールを実装する方法をまとめます。
ReactNativeプロジェクトの作成
まだプロジェクトがない場合は、作成します。
npx react-native init myApp
XCodeでSwiftファイルを作成する
ios/myApp.xcodeproj
にあるプロジェクトをXCodeで開きます。
NativeModules
フォルダを作成します。(任意)
NativeModules
フォルダ内に.swift
ファイルを作成します。
![スクリーンショット 2020-10-09 16.15.25.png](https://i2.wp.com/qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F664546%2Fb9259d5e-746b-d99d-2fbf-a9af40728c25.png?w=1320&ssl=1)
Objective-C Bridging Headerを作成する
.swift
ファイルを作成すると、XCodeからObjective-C Bridging Header
を作成するかどうか聞かれるの、作成する。
このファイルは、名前の通りSwiftファイルとObjective-C
ファイルをブリッジするものです。
ファイル名は変えてはいけません。
以下のように、Objective-C Bridging Header
に追記します。
myApp-Bridging-Header.h
// myApp-Bridging-Header.h
#import"React/RCTBridgeModule.h"
メソッドを実装する
手始めに、最も簡単なネイティブモジュールを実装してみましょう。
カウンターの値を変化させます。
Counter.swift
import Foundation@objc(Counter)
class Counter: NSObject {
private var count = 0
@objc
func increment() {
count += 1
print("count is \(count)")
}
}
メソッドをReactNativeから扱えるようにする
実装したメソッドをReactNativeから扱えるようにObjective-C
のファイルを作成します。
先ほど作成したSwiftファイルと同じ名前で、同ディレクトリに作成します。
![スクリーンショット 2020-10-09 16.51.20.png](https://i1.wp.com/qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F664546%2F65c74e95-f76b-3aeb-2d47-ba393478c492.png?w=1320&ssl=1)
作成したファイルには以下を追記します。
Counter.m
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(Counter, NSObject)
RCT_EXTERN_METHOD(increment)
@end
RCT_EXTERN_METHODについて
RCT_EXTERN_METHOD
に記述したメソッドは、ReactNativeで使用することができます。
メソッドに引数がない場合は、以下のように記述します。
RCT_EXTERN_METHOD(methodName)
引数がある場合は、以下のように記述します。
RCT_EXTERN_METHOD(
methodName: (paramType1)internalParamName1
)
例えば、increment
メソッドに引数が必要な場合swift
ファイルは以下のようになります。
@objc
func increment(_ num: Int) {
...
}
対応するRCT_EXTERN_METHOD
は以下のようになります。
RCT_EXTERN_METHOD(
increment: (Int)num
)
ReactNativeから呼び出す
NativeModules
をインポートし、クラス名.メソッド
で呼び出すことができます。
import { NativeModules } from 'react-native'
NativeModules.Counter.increment(1));
Event Emitterを扱う
iOSネイティブ側で起こるイベントをSunscribeしたい時には、RCTEventEmitter
を使用します。
Objective-C
のファイルに、以下を記述します。
#import "React/RCTBridgeModule.h"
#import "React/RCTEventEmitter.h"
@interface RCT_EXTERN_MODULE(Counter, RCTEventEmitter)
さらに、ブリッジファイルにも追記します。
CounterApp-Bridging-Header.h
#import "React/RCTBridgeModule.h"
#import "React/RCTEventEmitter.h"
Swiftファイルには以下を実装します。
sendEvent
: イベント名と、その内容を記述supportedEvents
: ReactNative側でリスナーを貼る時のイベント名を記述
@objc(Counter)
class Counter: RCTEventEmitter {
@objc
func increment() {
count += 1
print("count is \(count)")
sendEvent(withName: "onIncrement", body: ["count": count])
}
override func supportedEvents() -> [String]! {
return ["onIncrement"]
}
}
ReactNative側で、イベントリスナーで待ち受けます。
import {
NativeModules,
NativeEventEmitter
} from 'react-native'
const CounterEvents = new NativeEventEmitter(NativeModules.Counter);
CounterEvents.addListener(
"onIncrement",
(res) => console.log(res)
);
NativeModules.Counter.increment();
[警告] Module requires main queue setupへの対処
以下のような警告が出ることがあります。
Module requires main queue setup
これは、モジュールの処理をメインスレッドで行うか、バックグラウンドで行うか設定しなさいという警告です。
以下のように記述し、警告を消すことができます。
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
true
を返す: メインスレッドで処理false
を返す: バックグラウンドで処理
まとめ
ネイティブモジュールをSwiftで書く方法をまとめました。