ガジェット通信 GetNews

見たことのないものを見に行こう

JavaScriptとネイティブをつなぐ便利機能!──React Native ネイティブモジュールの作り方

DATE:
  • ガジェット通信 GetNewsを≫

bridgeとはどんな技術か

React Nativeに関する勉強会「React Native meetup#4」が、1月26日に渋谷のエンジニア&デザイナー向け無料イベントスペース「21cafe(ニイイチカフェ)」で開催された。

最初に登壇したのはキュア・アップ CTOの髙木健介(januswel)さん。

キュア・アップはアプリで病気を治療する未来を創造するというビジョンを掲げてビジネスを展開している医療ITベンチャーだ。

セッションタイトルは「discussion about bridge(邦題:詳解ブリッジ)」。
bridgeとは、JavaScriptの世界とネイティブアプリの世界をつなぐ橋のような役割をするものだ。

具体的にはネイティブモジュールを認識して使えるようにしたり、バンドルしたJavaScriptをロードして実行可能にしたり、JavaScriptとネイティブ関数のやり取りを仲立ちしたりする。

ネイティブモジュールとは、iOSならObjective-CやSwiftなどで書かれたもので、プラットフォーム特有の機能を使うことができる。

例えば地図や位置情報、NFCなどをJavaScript側から呼び出せるようにしたり、処理の重いタスクをネイティブ側で任せ、JavaScriptで受け取るというために使ったりする。React Nativeもネイティブモジュールで実装されている。

ではなぜ、ネイティブが必要なのか。例えばApache Cordovaを使えば、ネイティブっぽいUIを実装できるが、ネイティブが持つ本来のサクサク感の再現や、iOSの見た目を完全にエミュレートできないなどの問題がある。さらにiOSやAndroidがバージョンアップすると追随できないこともある。

とはいえ、ネイティブでの開発速度は遅くなりがちだ。画面要素をしっくりくる位置を探したいときに、書き換えてコンパイルするというループを繰り返す。

Titaniumというフレームワークではネイティブ側の機能は使えるが、画面部品の再配置でコンパイルが必要になりつらい思いをした人は多いだろう。

SCRIPTING NATIVEは、ネイティブのUIやロジックをスクリプト言語で扱えるようにしようという思想を持つ。だが次のような問題があった。
JavaScriptを描画用のスレッドでロックするような処理を走らせると、画面が固まったようになる
既存のプログラミングモデルは、リソースが競合する可能性があるためロックが起こりやすい。
マルチスレッドの場合、そのスレッド間のやり取りが固定的に発生して、オーバーヘッドになる。

これに対して、Facebookが提示した解決策が、bridgeである。bridgeはどのように動くのか、それを具体的にしたのが次の図だ。

立ち上がるとAppDelegeteに書かれている処理を行う。その途中でRCTRootViewが初期化されてbridgeを作成し、RCTBatchedBridgeがバンドルしたJavaScriptを触りにいく。

そしてJavaScript executorをセットして実行し、最後にCollectingでネイティブモジュールの認識をする。

ネイティブモジュールの作り方

ネイティブモジュールはどう作るのか。サンプルコードは次の通り(※Objective-Cで記述)。

@interfaceはクラス宣言

@implementationではメソッドを実装していく。RCTBridgeModuleに準拠していると、Reactネイティブ側がネイティブモジュールだと認識するのだ。

RCT_EXPOTRT_MODULE

実際にネイティブモジュールを作るときに、書かなければならないおまじない。これを書くことによってObjective-Cはマクロが使えるようになる。

スコープの変更

C言語はファイル単位でシンボルスコープを持っていて、それをこえるためにexternをつける。こうすると、コンパイルしたものが外から見えるようになる。

@#js_name

文字列の@マークとC言語のマクロの合わせ技。

load

iOSアプリの仕様になる。+ (void)loadと言うメソッドがあると、クラスがランタイムに認識された時点で、自らをBrideに登録するようになる。

RCTRegisterModule

RCTRegisterModuleがRCTBridgeModuleプロトコルに準拠していることを確かめて、配列にプッシュする。

RCT_EXPORT_MODULE

アプリが起動し、各クラスがロードされる。その際loadメソッドが自動的に呼ばれるため、そこで自らをBridgeへ登録しにいき、実際に使えるようになる、という仕組みだ。

ネイティブモジュールの初期化

ネイティブモジュールがインスタンスを作るときのbridgeでの処理の中身。selfはbridgeそのもの。きちんとそれをモジュール側に渡すことで、モジュール側からbridgeの機能を使えるようになる。

次にモジュールにさせることを見ていく。まずはメソッドを定義する。

RCT_EXPORT_METHOD

ここでは例としてaddEvent、つまりカレンダーに対して何か予定を追加することを定義している。

rct_export__142というところでは、実際のソースコードの行数とメソッドの回数を文字列化して登録する。これによりダブりがなくなる。

メソッドの認識

リフレクションみたいな形。クラスの中のメソッドを洗い出してすべて登録しに行く。実際にネイティブモジュールが認識したときにこの処理を行うので、どのモジュールがどんなメソッドを持っているかわかる。

ネイティブモジュール実行時の前提

React Nativeが裏でどう動いているのか。UIKitという描画や再配置をする機能が動くスレッドから、bridgeがJavaScriptの実行スレッドとモジュールごとにGCDキュー(スレッドのようなもの)をセットアップしていく。

JavaScriptからネイティブのファンクションを呼ぶときはどうするか。ネイティブモジュールはReact Native直下に入る。CalendarManagerからaddEventでさっき定義したものを呼ぶ。こうするとJavaScript側からMessageQueue.enqueueNativeCallでネイティブを呼び出すことができる。

ネイティブからJavaScriptを呼び出す方法は「コールバック」「Promise」「イベントを送る」の3つの方法がある。それぞれのコードは次の通りだ。



最後に、SCRIPTING NATIVEという発想は非常に強力な方法だ。React NativeはWebエンジニアがモバイル開発できるようになる点がフィーチャーされがちだが、ネイティブエンジニアにとっても開発効率を上げることにつながる。

今回紹介したコードを参考に、ぜひコードを書いてその使い勝手のよさを試してほしい。

リリースされたばかりのReact VRとはどんな技術か

続いて登壇したのは、besutomeさん。セッションタイトルは「React Native for React VR」。React Native Meetupを主催している。

React VRとは、2016年10月に開催されたOculusのカンファレンス「Oculus Connect 3」で発表されたReactフレームワークをVR向けに拡張したもの。Facebookが開発しており、12月13日にプレスリリースが発表された。

React VRはWebVRやブラウザで出力し、Webブラウザで動かす。WebVRのメリットはプラットフォームが固定されないこと。ただしネイティブVRよりも表現力は落ちる。

3Dは普通のプログラミングとは違う概念を持つ。3Dを作る際に必要な要素は以下だ。
シーン:3D上の空間。物体などを配置する
カメラ:3D空間の視点を決める。初期位置の視点をどこか決める。例えばOculus Riftなどのヘッドマウントディスプレイの場合、3D空間の位置を自分の位置とリンクさせて決める
光源:どこから光が当たっているかを指定する

ReactVRは公には公開されていない。ただnpm上ではアップデートされているので、そこでインストールすると追える。

static-AsettsはHello worldを作る際の背景となる。背景画像がうまく読み込まれていない場合は、Reactのバージョンが合っていない。そして、ブラウザのユーザーエージェントをスマホにすると動かないので注意が必要だ。

React VRはWebVR用の技術。Reactの知識と3Dの知識があれば、React VRは手軽に使える。さすがにまだ使えないが、今後に期待したい。

jsだけでつくるインスタグラム

一般発表の後は、一人10分間のLT発表が行われた。最初に登壇したのは、YutamaKotaroさん。LTタイトルは「jsだけでつくるインスタグラム」。

ことの経緯は、Swiftで書かれているアプリをReact Nativeができそうなことを言われた。受託したが、会議、会議でまったく話が進まない。

「これからの時代はReact Nativeだぜ」と言っても、社内でスルーされてしまう始末。そんなとき、React Native Showcaseを見ると、インスタグラムというイケているアプリがReact Native製になっていることに驚きを感じたのだ。

インスタグラムの持つ機能は
写真を撮る→ものすごくネイティブっぽい
おしゃれなフィルターをつけて写真を投稿する。
写真を見る
コメントとかファボをする。

そこでフィルター機能、つまり写真を撮り、おしゃれなフィルターをかけて保存する機能を実装することにした。

簡単にすませるため、画面遷移にreactnative router flux、写真を撮る機能はreact-native-cameraという有名なコンポーネントを使った。

react-native-cameraにはバーコードを読み取る機能も付いていた。自由にレイアウトを作成することや挙動をある程度の制御することもできる。

上は画面レイアウトを、下が写真を撮るところろを定義しているところ。

続いて写真の加工には、gl-reacti-naitiveというOpenGLをReact Nativeでバインディングしてくれるライブラリのキャプチャー機能を使った。続いて、gl-reactで写真加工の部分を作った。

写真加工の実装は図の通り。フィルターは自身で作ったコンポーネント。

フィルター側の実装。

shaderはGLSLで書いており、これで加工された結果が画面に出力される。

インスタグラムで実装されている機能はできた。しかしよくある写真撮りながらフィルターをかける機能は実装不可能だった。SurfaceのImageとしてreact-native-cameraを使うと黒い四角が出るだけになる。

ただ、実際に使ってみてReact NativeよりOpenGLがすごいという感想の方が大きくなった。触ったのはほんの入り口に過ぎないので、もっといろいろできるようになるはずだ。

scalajsでサーバとのやり取りをユニバーサル化する

続いて登壇したのは、しばたこさん。本業のアドテクではScalaを使っているという(Scala.jsは導入中)。LTタイトルは「scalajsでサーバとのやり取りをユニバーサル化する」。

フロントでも複雑なロジックを扱うことが求められている。近年、スマホファーストの時代ではWPAやネイティブアプリのようにスマホのみ(オフライン)でも動作することが求められてくる。

するとフロントエンドは、これまでのようにただJSONを受け取ってそれを描画するだけではなく、ドメインロジックや画面遷移を含む一通りの動作ができるようになる必要がある。

そこでフロントエンドでは複雑なロジックが散らばるのを避けたいという問題が起こってきた。複雑なロジックがプラットフォームごとに独立してあると、バグなくすべてをメンテナンスし続けることは大変。

そのため、全部で同じ言語で書き、同じコードベースを参照したいというニーズがある。それが以下である。
Web→React、Angular、Vue
スマホアプリ→React Native
デスクトップ→Electron

こうすればロジックは完全にJSで共通化でき、プラットフォーム特有の事情(UI層)だけを各プラットフォームで実装すればすむようになる。

各プラットフォームのフロントエンド間でのロジック共有を考える。フロント側のアーキテクチャはRedux前提で考えている。

つまり三行で表すと以下になる。
サーバ通信などの非同期処理も共有したい
Reducerでは扱えない
ActionCreatorで全部やってしまおう

共通化できるロジック
Domain Logic
サーバとの通信(fetch APIはユニバーサル)

無理すべきではないロジック
永続化など
Vies Logic(Loadingモーダルの制御など)
View Component

とりあえず上2つに取り組み、可能なら部分的に後者を進めていくことにした。

JSでサーバサイドを書くのは、個人的にはお勧めしない。一言語でいけるのならいいかも知れないが、それ以外ならロジックが散らばるからだ。

ScalaJSでフロントのDomain Logicを書くのもつらい作業だった。JSONシリアライズを共通化してライブラリに入れ、minifyしても400kB近くになってしまい、React Nativeで読むのには時間がかかってしまうからだ。(後日、対象ファイルだけBabelにかけないことで解決できると教えていただきました)

minifyしない場合、特定行をコメントアウトしないとimportでこけてしまう。

Reduxの仕組みに載せてもフロント側でのロジックの共有はできるので、サーバとフロントでのモデルの共通化はお勧めしない。

LT時間のオーバーを防ぐタイマーをReact Nativeで作る

最後のLTはdeadcheatさんによる「どうしてもLT時間をいつも過ぎてしまう俺達は」。deadcheatさんはLT大会を主催しており、昨年1年間で60本のLTを見てきた。

LTには次のようなあるあるがある。
5分で納める気がない人もいる
過ぎても普通に話しちゃう
(司会側の意見)止め時と止め方が難しい
(司会側の意見)別にいいんだ、いいんだけど

かつてはドラを鳴らす人がいたりしたが、今では批判の対象になってしまうこともあり、絶滅が危惧されている。そこでドラがなければ音を鳴らせばいいじゃないかということで、
タイマー時間に合わせて音を鳴らす。
音楽を鳴らして狙ったタイミングに狙った部分に到達するようにならしはじめての時間を指定可に
これによって制限時間の到来がわかる

そして焦らせるiOSアプリをReact Nativeで作ることにした。使ったモノは以下の通り。
音を鳴らす(zmxv/react-native-sound)


タイマー機能(fractaltech/react-native-timer)


orientationを固定(yamill/react-native-orientation)

ひっかかりどころとしては、急にXcodeがエラーをはいてビルドが失敗したり、凝ったレイアウトをやろうとすると難易度が急激に上がること。だが、React Nativeが面白さを知れたのがすごくよかった。

次回react-native meetup#6は3月16日開催!

こうしてReact Native勉強会は終了。初心者でも参加しやすいよう、セッションごとに「この技術を使ったことがあるか」という問いかけが行われていた。

React Nativeには触ってみたいという人は、ぜひ、次回の勉強会に参加してみよう。

カテゴリー : デジタル・IT タグ :
CodeIQ MAGAZINEの記事一覧をみる ▶
  • 誤字を発見した方はこちらからご連絡ください。
  • ガジェット通信編集部への情報提供はこちらから
  • 記事内の筆者見解は明示のない限りガジェット通信を代表するものではありません。