[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型も受け渡せるのは楽でいいですねえ。

今回、タイトルが(iOS編)です。

これで気づいた人もいるかもですが、明日はネイティブモジュール(Java)を使う[Android編]をやります。多分。

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

← 1日目 centos7+nginx+php-fpm+php7な新しい感じの環境を構築

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


とりあえずreact-nativeをすぐ使いたい記事。

環境は一つ前のMacBook Proです。

https://facebook.github.io/react-native/docs/getting-started.html

とりあえずはここに沿っているだけなのでこの記事見るよりこっちのほうがいいかも。
で、ハマったところを紹介していきます。
こういうの、あまり使ったことないと時間かかったりすると思うので。

brew install node
brew install watchman
npm install -g react-native-cli

Androidビルド

export ANDROID_HOME=~/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools

Android SDKなんかがうまいこと設定されていればこれで準備OK。

丁寧にパスを通す方法も載ってます。やさしいですfacebook。

ちなみにANDROID_HOMEは通してなかった場合も結構あると思うので、ちゃんと通しましょう。

通さないとビルドに失敗します。ここもハマりポイント。

react-native init testreactnative
cd ./testreactnative
react-native run-android

testreactnativeというフォルダが作成され、テンプレ状態のものが作られるので、そのままビルド。
で、駄目。

A problem occurred configuring project ':app'.
> failed to find Build Tools revision 23.0.1
BUILD FAILED

どうやら、現状はbuild tool 23.0.1でしか動かないっぽい。
つーか普通に書いてあったがな。単なる見落とし。
つか普通にbuild.gradleを変更すればできる気がしますが、素直に23.0.1を入れて再挑戦。

Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Failed to establish session

今度はなんだろう。インストールに失敗した模様。ちゃんとUSBデバッグもやっているのだが・・・

と思ったらこれはOS特有問題で、MIUIのデフォルト設定の場合こうなるとのこと。

ちょうどMIUI8のredmi note 3 proを使っていました。

com.android.ddmlib.InstallException: Failed to establish session react-native

Developer OptionのTurn on MIUI optimizationをオフ。これは外部からのインストールを防ぐ機能なのですが、今回はそれが邪魔した模様。

testreactnative

他に、-bash: adb: command not foundとか出てたのでPATHが設定できてなかったという問題もありました。
後はAndroidもiOSも初回は起動しても表示に多少時間がかかる模様。早とちりだと見落とします。
まあいいや、次はiOSだ!

iOSビルド

react-native run-ios
reactnative-ios

iOSはXcodeの新しいものが入っている環境なら、問題なくエミュレータが立ち上がりました。

しかし、実機の場合はXcodeから実行しないと駄目なようです。

testreactnative/ios/testreactnative.xcodeprojをXcodeで開き、プロビジョニング設定後そのままRunすればOK。

特に詳しい設定もいらなそうです。ちょっと試すならこっちが楽ですね。

また時間があったら試してみます。ネタを続けるために次回に続く。


3日目→