React Nativeネイティブモジュール(Swift/iOS)を使う

author potpro(ぼとぷろ)
2016/12/08

React Nativeネイティブモジュール(Swift/iOS)を使う

potproject.net Advent Calendar 20169日目の記事です。


react-nativeをとりあえずすぐ実機で動かしてみる

で、とりあえず実機で動くかの確認は取れました。 で、今回は、React Nativeとネイティブコードの連携をやってみたいと思います。
React NativeはiOSやAndroidを共通のJavascriptで書いてマルチプラットフォームでしかもネイティブに動くよ!というのがウリです。
しかし、当然ながら、SwiftやObjective-Cを使うようなものも必要になります。カメラとかプッシュ機能。
まあメジャーなものは用意されてる場合もあるんですが、マイナーなものは無かったりするので、
自分で作ってね!ってことでネイティブモジュールという機能で補完できるようになってます。

Cordovaを思い出す。しかしこっちは完全ネイティブなのだ。

このあたりを参考にしました。しかしまあやっぱり例によってSwift3.0なので結構コード変えてたりもします。 React NativeでNative機能をSwiftで書いて使うには
Native Modules

Objective-Cは全く分からないのでSwiftです。でもブリッジしなくてはならないので多少はObjective-Cは避けられないんだなあこれが。
:原則、React NativeはネイティブモジュールにSwiftを使うように設計されていません。生成されたプロジェクトもObjective-C用で生成されます。
なので、使うにはObjective-CとSwiftを連携させる必要があります。

swiftでモジュールを作成

まずは、モジュールを作成します。ちなみにこれだけだとまだ動きません。 端末の時間を取得してAny(Dictionary)型でコールバックを返します。
どうやらReact Nativeのjavascriptにデータを受け渡す際は必ずcallbackなのかな。 ちなみにPromiseも使えます。

** ReactNativeModule.swift **

import Foundation


@objc(ReactNativeModule)
class ReactNativeModule: NSObject {
    
  @objc func callbackMethod(_ callback: RCTResponseSenderBlock) -> Void {
    // システムのカレンダーを取得
    let cal = Calendar.current
    // 現在時刻のDateComponentsを取り出す
    var dataComps = cal.dateComponents([.year, .month, .day, .hour, .minute], from: Date())
    let object = [
      "year":dataComps.year!,
      "month":dataComps.month!,
      "day":dataComps.day!
    ]
    callback([object])
  }
}

配列で返しても大丈夫なの?と思いでしょうが、ちゃんとReact Native側はjavascript Object型で受け取ってくれます。優秀です。   callbackMethodの第一引数が無名関数がなっているのは、このあたりの問題から。認識してくれないのです。Swift3.0での変更点となります。 Got “is not a recognized Objective-C method” when bridging Swift to React-Native - stackoverflow

Objective-Cでエクスポートする

** ReactNativeModuleBridge.m **

#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(ReactNativeModule, NSObject)

RCT_EXTERN_METHOD(callbackMethod:(RCTResponseSenderBlock)callback)
@end

実際はこのObjective-Cが読み込まれます。これが上で書いたSwiftコードの橋渡しになります。
Objective-Cの文法は相変わらず割と理解できていない。

** testreactnative-Bridging-Header.h **

#import "RCTBridgeModule.h"

RCTBridgeModule.hというObjective-CのコードをSwiftで読み込むために設置。 このあたりに関してはググるといっぱい出てくるため割愛。

React Native側のソースはこんな感じ。画面の更新もしたかったのだが、結構時間掛かりそうで・・・ただログ出すだけ。
** index.ios.js **

import React, { Component } from 'react';
import {
  NativeModules,
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';
var nm=NativeModules.ReactNativeModule;
nm.callbackMethod(function(res){
    console.log(res);
});

これでデバッグ画面からログが確認できました。
ちゃんと{"year":2016,"month":12,"day":9}と出てきます。Any型も受け渡せるのは楽でいいですねえ。