この記事ではCocoapodsで導入したライブラリをReact Native側から呼び出して利用する方法についてご紹介します。
今回は、今更Objective-CでNative側のコード書くのもなんだかなー、と思いSwiftで書きました。
React Native側のボタンを押すと、Whisperというライブラリが呼び出される、ということを試しました。
こんな見た目の奴です。
Whisper
https://github.com/hyperoslo/Whisper
今回のソースはこちらに置いてあるのでよければ参考にしてください。
https://github.com/kouchi67/CocoapodsReactBridge
では、ハマったポイントも含めて説明していきます。
環境
- OS: El Capitan (10.11.6)
- Xcode: 8.1
- react native 0.38.1
Project作成
React NativeのProjectを作成します。
$ react-native init CocoapodsReactBridge
Cocoapods導入
Cocoapodsを導入していきます。
$ cd CocoapodsReactBridge/ios
$ pod init
$ vim Podfile
platform :ios, '10.0'
use_frameworks!
target 'CocoapodsReactBridge' do
pod 'Whisper', '~> 3.1'
end
これを書いている現在では、 Whisperの最新Versionは 4.0 でしたが、
Swift 3 でビルドできなかったので、 Swift 2.3 で動作する Version を導入しています。
Podfileの記述が完了したら、ライブラリをインストールします。
$ pod install
pod installが完了したらXcodeでプロジェクトを開きます。
$ open CocoapodsReactBridge.xcworkspace
Bridgeクラスの作成
Native側の処理をReact Nativeから呼べるように準備します。
以下のファイルを作成します。
- ReactWhisper.m
- ReactWhisper.swift
- CocoapodsReactBridge-Bridging-Header.h
CocoapodsReactBridge-Bridging-Header.h
に関しては、
Swiftファイルを作成するときに「Bridging-Headerファイル作る?」って聞かれるので、その際に作成しましょう。
順番にファイルを解説します。
import UIKit
import Whisper
@objc(ReactWhisper)
class ReactWhisper: NSObject {
@objc func showWhisper(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
dispatch_async(dispatch_get_main_queue()) {
let murmur = Murmur(title: "Called Native Modules !!")
show(whistle: murmur, action: .Show(2.0))
}
resolve([])
}
}
swiftファイルは、ライブラリを呼び出す本体のクラスとなります。
クラスとメソッドを宣言するところで、 @objc
とつけることで、Objective-Cから呼び出せるようになります。
React NativeとNative ModulesのBridging部分はObjective-Cで書く必要があるため、 @objc
が必要です。
(Bridging部分もSwiftで書けるやり方があれば教えてください・・・)
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
@interface RCT_EXTERN_MODULE(ReactWhisper, NSObject)
RCT_EXTERN_METHOD(showWhisper: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end
ReactWhisper.m
では、React NativeとNative ModulesのBridgingについて記述します。
RCT_EXTERN_MODULE
RCT_EXTERN_METHOD
を使うことで、Native側のクラスやメソッドをReact Nativeから参照できるようになるようです。
今回は ReactWhisper.swift
で記述した showWhisper
メソッドを呼び出せるようにします。
#import "RCTBridgeModule.h"
このファイルは、SwiftからObjective-Cのクラスを参照するために必要です。
Swiftから RCTPromiseResolveBlock
など参照する必要があるため、必要です。
Swift 2.3で動くようにする
今のままではビルドが通らないので、Swift 2.3で動作するように設定を変えます。
ビルド設定にある Use Legacy Swift Version
という項目を Yes
に修正します。
以下のようになります。
これでビルドが通るようになりました。
Native側の修正はこれで一旦終わりです。
React NativeからNative Modulesを呼び出す
index.ios.js
に必要最低限なコードが記述されてますが、ここにWhisperを呼び出すためのボタンを追加します。
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
Button,
NativeModules,
View
} from 'react-native';
export default class CocoapodsReactBridge extends Component {
onPressShowWhisper() {
NativeModules.ReactWhisper.showWhisper();
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
<Button
onPress={this.onPressShowWhisper}
title="show Whisper"
color="#841584"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('CocoapodsReactBridge', () => CocoapodsReactBridge);
NatveModules
をimportして使用しているところがミソです。
ここまで書けたらXcodeからRunします。
iOS Simulatorが起動できたら、 show Whisper
ボタンを押してみましょう。
Status BarのところにWhisperが表示されていますね。(背景が白でわかりにくいですが・・・)
無事に呼び出すことに成功したようです。
Warningの対応
useNativeDrive is not supported とか表示されてます。
これについてちょっとよくわかってないですが、デフォルトではNative側で動作するアニメーションは対応していないようです。
Simulator上では普通に動いてますが、消したほうがよいのでしょう。。。
とりあえずぐぐったらこんなページを発見しました。
- find RCTAnimation.xcodeproj from node_modules/react-native/NativeAnimation folder
- open xcode
- open project navigator (folder icon from the left)
- drag & drop RCTAnimation.xcodeproj from the previous step to Libraries folder
- Expand RCTAnimation.xcodeproj in project navigator and find libRCTAnimation.a file inside of Products folder
- click on your project name on top of project navigator and go to Build Phrases tab
- drag and drop libRCTAnimation.a to Link Binary With Libraries
- Clean and run :)
こちらの手順に従って、 libRCTAnimation.a
とやらをXcodeのProjectに追加してあげると良いようです。
RCTAnimation.xcodeprojとやらをまず探します。
私の環境では以下に保存されていました。
CocoapodsReactBridge/node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj
中を確認しても .a
ファイルが存在しませんでした。
手順とは違いますが、このプロジェクトでビルドすると .a
ファイルが作成できるはずなので、Xcodeで開きます。
$ cd CocoapodsReactBridge/node_modules/react-native/Libraries/NativeAnimation
$ open RCTAnimation.xcodeproj
開いたらビルドしてみます。
無事に作成されたようです。
あとは、このファイルをCocoapodsReactBridgeプロジェクトにドラッグ&ドロップで追加します。
こんなファイル構成になりました。
これでもう一度Runしてみると、Warningが消えていることが確認できると思います。
ちゃんとWarningが消えてますね!
注意点
swiftファイルの以下の記述について補足します。
dispatch_async(dispatch_get_main_queue()) {
let murmur = Murmur(title: "Called Native Modules !!")
show(whistle: murmur, action: .Show(2.0))
}
dispatch_async( ...
という記述がありますが、これはUI操作に関する処理がある場合に必要となります。
iOSでは GCD
という機構が用意されており、それを使って特定の処理のみをメインスレッドで処理することができます。
iOSでは、UI操作に関する処理はメインスレッドで処理することがルールとなっています。
メインスレッド以外でも処理はできますが、想定した動作とならないことが多いです。
(即座に反応しない、所定の位置に表示されない、そもそも表示されない、などなど)
最後に
いかがでしたでしょう?
Cocoapodsで導入したライブラリをReact Nativeから呼び出す、ということをやってみました。
Androidとコードが共通化できるReact NativeではiOSライブラリを利用することはあまりないかもしれませんが、
既存のiOSライブラリには便利なものがたくさん公開されています。
また、今回はSwiftで書きましたが、Swiftはまだまだバージョンアップが頻繁で古いコードではビルドが通らないこともよくあります。
ライブラリが追従しておらずビルドできないというケースも珍しくありません。
そのため注意が必要ではありますが、活用できる資産はどんどん使っていけばよいかなと私は思います。
もしかしたらですが、Objective-Cで書いたほうがビルドエラーに悩まされる事は少ないかもしれません。
(Swiftほど構文の変化はないはずなので)
Advent Calendarの投稿に遅れてしまいましたが、私の記事はこれで終わりです。