Native Modulesについて
React Nativeを使えばJavaScriptでネイティブアプリを作ることができます。
ただ、各OSの全APIを呼べるわけではないですし、Objective-CやSwift書いた既存のコードを使いたい場面もあります。
そのために用意されているNative Modulesという仕組みを試してみました。
React Native official document: Native Modules
minimumなsampleはここが分かりやすかったです。
Objective-C側で作成したNSStringをReact Nativeで出力するだけです。
React Native - Super Simple Native Module Example
Native Moduleの実装
NSURLProtocolを使って広告ブロックを実装
参考記事: iOSアプリ中のUIWebviewで広告ブロック(Objective-C)
上記の記事ではNSURLProtocolを使ってUIWebviewに広告ブロック機能を実装しています。
React NativeにはNSURLProtocolを呼ぶAPIが存在しないため、Native Moduleを使ってWebViewに広告ブロック機能を実装してみます。
Objective-Cのコードでは、広告ブロックのロジックを実装したFilteredURLProtocolを以下のコードでNSURLProtocolに設定しています。
*詳しくは元記事をご覧ください。
[NSURLProtocol registerClass:FilteredURLProtocol.class];
これをReact Nativeから呼んであげればOKということです。
ブリッジ用Classの作成
ブリッジ用にAdblockManager Classを作成します。
ブリッジで使用するClassはRCTBridgeModuleプロトコルを継承する必要があります。
#import "RCTBridgeModule.h"
@interface AdblockManager : NSObject<RCTBridgeModule>
@end
React Nativeから呼び出せるメソッドの作成にはRCT_EXPORT_METHOD()マクロを使います。
ブリッジ呼び出ししたメソッドは非同期なので返り値が必要な場合はcallbackで渡します。
#import "AdblockManager.h"
#import "FilteredURLProtocol.h"
@implementation AdblockManager
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(setAdblock:(RCTResponseSenderBlock)callback)
{
[NSURLProtocol registerClass:FilteredURLProtocol.class];
NSString* result = @"setAdblock";
callback(@[result]);
}
RCT_EXPORT_METHOD(unsetAdblock:(RCTResponseSenderBlock)callback)
{
[NSURLProtocol unregisterClass:FilteredURLProtocol.class];
NSString* result = @"unsetAdblock";
callback(@[result]);
}
@end
React Native側からの呼び出し
NativeModulesをimportします。
WebViewのmount時に登録、unmount時に登録解除します。
import React, {Component} from 'react-native';
import {AdblockManager} from 'NativeModules';
const {
StyleSheet,
View,
WebView
} = React;
export default class SimpleWebView extends Component{
componentWillMount(){
if (this.props.adblock){
AdblockManager.setAdblock((result) => {
console.log(result);
});
}
}
componentWillUnmount() {
if (this.props.adblock){
AdblockManager.unsetAdblock((result) => {
console.log(result);
});
}
}
render(){
return (
<View style={styles.container}>
<WebView url={this.props.url} startInLoadingState={true}/>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1
}
});
メソッドを呼び出すだけならこれだけです。
思っていたよりもかなり簡単でした。
UIが絡むとどうなるんでしょうね。
ソースはこちら。
https://github.com/enu-kuro/ReactNative-Adblock-WebView
React Nativeを触ってみた感想
起動させるまでがしんどかったです。
githubからサンプルコードを落としてきても簡単には起動してくれません。
ググッてググッてゴニョゴニョしてやっと起動できましたが、心に余裕がないと起動する前に諦めてしまいそうです・・・。
一通り罠を通ったので最近は起動まではなんとかできることが多くなりました。
作っていると謎の挙動によくぶち当たります。
情報がまだ少ないので解決できないで別の方法で回避しなくてはならないことがあったりします。
例えばNavigatorIOSのtitleやpropsが更新されてくれないとか。
そのせいでサンプルに作ったコードもやや無理やりな実装になっている部分があります。
(現在FacebookはAndroidでも使用できるNavigatorを使用するようにしておりNavigatorIOSのメンテはしていないとのことです)
JavaScriptだけで簡単なアプリは作れてしまいますが、iOSでの実装の仕組みを知っていないと細かい挙動を実装するのは難しいかなと思いました。
ListViewのDataSourceとか。
StoryBoardが使えないので複雑なアプリになるとUIや画面遷移管理はしんどそうです。
CSSかけないのでつらいです。
iPhone6で操作する分にはパフォーマンスはまったく気になりませんでした。
(追記: と思いましたがいろいろ触っていると挙動がぎこちなくなってきました。使用メモリがどんどん増えているのでメモリリークしているようです。メモリはまったく意識せずに作っていたのですがどうすればいいのでしょうね・・・。)
お疲れ様でした。