モバイルアプリ開発におけるWeb技術の可能性を探る特集・第一弾でご紹介するのはReact Native。その生い立ち、チュートリアルから、コードベースやUIを記述する言語、パフォーマンスまで解説します。
概要
React Nativeは2013年にFacebook社内のハッカソンで生まれたプロジェクトです。2014年にiOSアプリのFacebook Ads ManagerをReact Nativeを用いて開発し、2015年3月にオープンソースとして公開されました。そして、半年後の2015年9月にAndroidをサポートし、今年のF8では、MicrosoftがReact NativeでWindowsプラットフォーム(PC, Mobile, Xbox)の開発ができます、という発表がありました。また、macOSアプリの開発、Ubuntuアプリの開発など他のプラットフォームの開発もできるようにするプロジェクトも走っているようです。
React Nativeのコンセプトは、”Write Once, Run Anywhere”(一つのコードでどこでも動く)ではなく、”Learn Once, Run Anywhere”(一度学べば、どこでも動かせる)です。一度React Nativeを理解すれば、どのプラットフォーム上でも同様のコーディングを行って、プラットフォームネイティブなアプリケーションを開発することが出来ます。Githubのスター数は35,000を超え、リリースの頻度も多く、グローバルな視点でみると、非常に熱い技術であると言えます。
(編集部注: 「Write Once, Run Anywhere」は、あらゆるプラットフォームで同一のコードが動作するという、Javaが喧伝していたコンセプトです)
Hello, World
以下は、macOS上で開発することを前提としたチュートリアルです。
React Native開発に必要なものは、 Node.js, React Nativeのコマンドラインツール、Watchman(Facebookによって開発されているファイル監視ツール)になります。Homebrewで下記のようにインストールします。
$ brew install node
$ brew install watchman
$ npm install -g react-native-cli
$ npm -v
3.6.0
$ node -v
v5.0.0
iOS
iOSの場合はXcodeが必要です。XcodeはMac App Storeからインストールしましょう。 準備が整ったところで、プロジェクトを作って走らせてみます。
$ react-native init AppByWebTech2016
$ cd AppByWebTech2016
$ react-native run-ios
Android
Androidの場合は、Android Studioが必要なのでインストールしておきます。アプリの起動には、実機をUSBで繋げておくか、Emulatorが必要なので適宜用意します。今回は、Genymotion(個人利用は無料)を起動しておいて, 下記のコマンドを走らせます。
$ react-native run-android
NativeBaseを使う
これだけだと味気ないので、NativeBaseを用いてUIを作ってみたいと思います。NativeBaseはWebでいうBootstrapのようなものです。ボタン等のUIを自分でデザインする必要がなく見栄えの良いインターフェイスを簡単に作ることができます。
では、NativeBaseをインストールしてみましょう。(※react-nativeのバージョンは0.32)
$ cd AppByWebTech2016
$ npm install native-base --save
$ react-native link react-native-vector-icons
index.ios.jsを下記のように変更すると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import React, { Component } from 'react'; import { Container, Content, Button, Header, Title, List, ListItem, Badge } from 'native-base'; import { Col, Row, Grid } from 'react-native-easy-grid'; import { AppRegistry, Text, View } from 'react-native'; class AppByWebTech2016 extends Component { render() { return ( <Container> <Header> <Title>Hello World</Title> </Header> <Content> <List> <ListItem > <Badge>1</Badge> </ListItem> <ListItem> <Badge primary>2</Badge> </ListItem> <ListItem> <Text>List 3</Text> </ListItem> </List> <Grid> <Row> <Col> <Button style={{margin:10}}> Click Me! </Button> </Col> <Col style={{backgroundColor: '#204d74'}}> <Text style={{fontSize: 20, color: 'white', margin: 10}}>React Native!</Text> </Col> </Row> <Row style={{backgroundColor: '#00c497'}}> <Text style={{fontSize: 24, color: '#333', margin: 10}}>Learn once, Write anywhere</Text> </Row> </Grid> </Content> </Container> ); } } AppRegistry.registerComponent('AppByWebTech2016', () => AppByWebTech2016); |
スタイルのことを気にせずボタンを作ることができ、グリッドなども簡単に使用できます。プロトタイピングなどにはもってこいのモジュールとなっています。
プラットフォーム/ライブラリの特徴
項目 | 説明 |
---|---|
対応プラットフォーム | iOS, Android, Windows(PC, Mobile, Xbox), macOS※, Ubuntu※ |
コードベースは(ほぼ)完全に統一できるか? | 統一できない(実際は7,8割は共有できる) |
UIを記述する言語 | JavaScript, React, スタイルシート |
UIはネイティブか、Webか | WebViewではない。ネイティブもしくはJSとのハイブリットで実現 |
パフォーマンス | ネイティブ同等 |
ネイティブな機能を呼び出せるか? | APIを通じて自由に呼び出せる |
※コミュニティがサポート
以下に、上の表を補足します。
対応プラットフォーム
私が確認したのはiOSとAndroidですが、前述した通り、下記のプラットフォームでReact Nativeの方法でNativeアプリを動かすことができるようです。
- iOS
- Android
- Windows(PC, Mobile, Xbox)
- MacOS
- Ubuntu
- Browser※1
※1React Native for WebというChrome, Firefox, Safari >= 7, IE 10, Edge.のBrowser上でReact Nativeコードを動かすというプロジェクトさえ存在します。
コードベースは(ほぼ)完全に統一できるか?
残念ながら完全には統一できません。現実には各プラットフォームの作法(例えば、iOSはtabbarは下部、Androidは上部) が存在し、完全に同じにすべきかというのは別の議論が必要な気がします。ただ85%は同じコードを再利用できたといった話もないわけではありません。ビジネスロジック部分は統一させ、UIは個別のコードで実現するというのが、コードの再利用+ネイティブの作法の両面を考慮に入れたReact Nativeの流儀だと言ってよいでしょう。
UIを記述する言語
コードはJavaScript(ES6), コンポーネントはReact, デザインはスタイルシートを用いて記述していきます。 スタイルシートはCamel記法となるのでCSSのbackground-colorがbackgroundColorという属性になり、非常に馴染みの深いものになります。 React Nativeは、レイアウトを行うためにFlexboxを用います。最近のWebブラウザでは広くサポートされているので、ご存じの方も多いことでしょう。
ES6やReactは確かに学習コストがあります。しかし、Web業界で生きているひとは少なくとも触っておくべき技術でしょう。React Nativeでのアプリ開発の経験が逆にWebに生かすというようなことも起きるかもしれません。
パフォーマンス
ネイティブのコンポーネントを利用する限り、ネイティブと同等のパフォーマンスになると言ってよいでしょう。パフォーマンスの最適化を開発者に意識させないようにすることがReact Nativeが実現しようとしていることです。
しかし、公式のドキュメントによると、現実には何点か難しい箇所があると述べられています。
React Nativeは下図のようにスレッドが走っています。
React.js Conf 2016 – Tadeu Zagallo – Optimising React Native: Tools and Tipsより転載
端的に言うと、JavaScriptのビジネスロジックが動くJSスレッド上で時間のかかる処理を行うと、UI出力時に遅延が起きてしまうケースがあります。 もしパフォーマンスに影響が出たときは、このJSスレッドを意識してコードを見直してみる必要があります。
今年のReact Confの発表でも言及されていましたが、Facebookは社内にReact Nativeのパフォーマンスチームを作ってパフォーマンスを継続的に改善しています。彼らがリソースを割いて注力している部分でもあるので、これからも改善されていく部分だと思います。
UIはネイティブか、Webか
WebViewを使わないという観点でWebではありません。ネイティブのライブラリを呼び出すAPIが多いですが、JSとのコードのハイブリッドといったイメージでしょうか。UIの動作は非常にネイティブライクです。
ネイティブな機能を呼び出す方法
React Nativeとネイティブ間を受け渡すようなAPIが用意されています。 これを使うことで容易にネイティブからの値を受け取ったり、受け渡したりすることができます。 この程よい抽象化があることで、現行のiOSアプリにも組み込むことが可能となります。 実際、Facebookのアプリでも一部はReact Nativeで書かれてると言っています。
今回は非常に簡単な例ですが、React Native側で2つの数字を渡しネイティブ側で和を求めて返すメソッドを用いて説明します。(今回はiOS)
まずは、Xcodeでios/AppByWebTech2016.xcodeproj
をOpenし、新規作成からCocoaClassを選び、適当なSubクラスを選びます。
すると、AppNativeSum.hとAppNativeSum.mができると思います。 このファイルを下記のように書き換えます。RCT_*(ReaCTの略らしい)の接頭辞がついたAPIをReact Nativeが提供しています。Callbackを用いて返す方法もありますが、今回はPromiseを使います。
1 2 3 4 5 |
#import "RCTBridgeModule.h" @interface AppNativeSum : NSObject <RCTBridgeModule @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#import "AppNativeSum.h" @implementation AppNativeSum RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(sumNumber:(int)val1 val2:(int)val2 resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSInteger val3 = val1 + val2; NSString *val3Str = [NSString stringWithFormat:@"%ld", (long)val3]; resolve(val3Str); } @end |
このメソッドの戻り値は、JavaScriptのPromiseです。通常のPromiseと同様に、async/awaitを使って非同期処理の結果を処理することも可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import { NativeModules } from 'react-native'; var AppNativeSum = NativeModules.AppNativeSum; class AppByWebTech2016 extends Component { constructor(props){ super(props); this.state = { sum: 0 } } async componentDidMount(){ let sum = await AppNativeSum.sumNumber(1,2); this.setState({sum: sum}); } ... |
ネイティブコードに精通していれば、公式ドキュメントにあるAPIに渡すだけで容易にReact Nativeとネイティブのやりとりができます。
まとめ
マレーシアの友人が、彼の国では企業がReact Nativeエンジニアの採用をしていると言っていました(中国でも似たような状況にあるとか)。 日本でもちらほらReact Nativeを採用する会社がでてきているようです。一年半以上経過し、リリース速度が多少遅くなっているといってもどんどん新しいバージョンが出てきて、かつよりよいUIモジュールもでてきています。今こそ、React Nativeを始めてみてはいかがでしょうか。