iOS・Android

【ReactNative】SwiftでNativeModuleを書く

iOS・Android
この記事は約6分で読めます。

はじめに

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

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

作成したファイルには以下を追記します。

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で書く方法をまとめました。