React+ReduxなネイティブアプリをReact Nativeでシュッと作る

reactnative.002

こんにちは! Pairsチームの太田です。

梅雨空が続く今日この頃ですが、いかがお過ごしでしょうか。

突然の豪雨への備えは常に持っておきたいですね。

ところで、私の前回の記事ではJavaScriptでクロスプラットフォームアプリを書く魅力とReact Nativeがよさそうという内容をお伝えしました。

その後、実際にReact Nativeを使ってサンプルアプリをシュッと書いてみましたのでレポートします。

React Nativeのおさらい

React NativeはFacebookが開発しているプロダクトで、ReactのアーキテクチャとJavaScriptでネイティブアプリを書くことができるフレームワークです。

Reactのアーキテクチャを他のプラットフォームの上でも走らせることができるので、learn once, write anywhere というポリシーを謳っています。

開発元のFacebook自体がいくつかの自社のアプリをReact Nativeでリリースしているだけでなく、サードベンダーからも多くのReact Nativeアプリがすでにリリースされています。

Apps using React Native

React Nativeアプリをシュッと始める

よーしパパReact Nativeでクロスプラットフォームアプリ作っちゃうぞー、と意気込んでみましたが、すぐにウッとなってしまいました。

  • iOSとAndroidのどっちを軸にしてやるのがいいの?
  • 並行して進めるのがいいの?
  • プラットフォームに依存したUIコンポーネントはどう扱えばいいの?

などなどがモヤモヤしてきてしまって道を見失ってしまったのです。そこで作戦を考えました。

  • WebのReactアプリをベースにして、React Native化する
  • UIコンポーネントはReact Nativeが提供するプラットフォーム共通に抽象化されたCore Componentsだけを使う

この方針で進めることにしました。

この方針を取った理由は、WebのReactアプリを作り上げてから、プラットフォーム依存の部分をReact Nativeに合わせて変更する開発フローが完成への最短ルートだと思ったためです。

DDDのレイヤ化アーキテクチャの観点からみると、React Webアプリのアプリケーション層・ドメイン層はReact Nativeアプリで再利用できることがわかります。

reactnative.001

対して、UI層・インフラストラクチャ層はプラットフォーム依存の対応が必要になります。

まずはReact Webアプリですべての層を実装したあとでアプリをReact Nativeに載せて、UI層・インフラストラクチャ層をネイティブ化するやり方を取りました。

React WebアプリをReact Native化する

WebのReactアプリでいい感じのものがあったので、これをサンプルアプリのベースにしました。

React+Reduxで書かれたポモドーロタイマーです。

Cu7ious/React-n-Redux-Pomodoro-Clock

reactnredux

React Nativeのインストールは完了した前提で進めます。まずはReact Nativeプロジェクトを生成します。

$ react-native init Pomodoro

作成したプロジェクトをReact+Reduxアプリに仕立てる手順はこちらが参考になりました。

React Native Navigator Experimental Part 2 — Implementing Redux

これでReact-n-Redux-Pomodoro-ClockのコードをPomodoroプロジェクトへ移管できました。

UIをネイティブ化する

React-n-Redux-Pomodoro-ClockではHTML要素で実装されているUIをネイティブ化します。

今回はReact Nativeが提供するプラットフォーム共通のCore Componentだけを使ってみることにしました(シュッと作りたいため)。
Core ComponentにはHTML要素のimgにあたるImageやdivにあたるViewなどが用意されています。

また、CSSと同じプロパティでスタイル定義できる仕組みをReact Nativeが持っているので、Webページと近い感覚でUIを書くことができます。

下記はボタンのUIの例です。

render () {
let p = this.props
let callback = p.disabled ? this._pauseTimer : this._startTimer
let text = p.disabled ? "Pause" : "Start"
return (
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', paddingBottom:30 }}>
<TouchableOpacity onPress={callback} style={styles.button}>
<Text>{text}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._clearTimer} style={styles.button}>
<Text>Clear</Text>
</TouchableOpacity>
</View>
)
}
// スタイル定義
const styles = StyleSheet.create({
button: {
width: 100,
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
音を鳴らす
React-n-Redux-Pomodoro-Clockはポモドーロタイマーとしてカウントダウンしているときとタイムアップしたときに音が鳴る機能があります。これはHTML5のAudioをJavaScriptから利用しているのですが、ネイティブアプリではそのままで動作しませんのでプラットフォーム対応が必要です。
少し探してみると、react-native-soundというnpmパッケージが見つかりました。
zmxv/react-native-sound
これを使って簡単に音を鳴らすことができました。インストールと設定の手順がREADMEに詳しく説明されていてネイティブアプリ環境に疎い私でもすぐ使うことができたので大変ありがたかったです。
音を鳴らすコードはこのようなイメージになります。
import Sound from 'react-native-sound'
// snip...
this.tickSound = new Sound('tick_tock_sound.mp3', Sound.MAIN_BUNDLE, (e) => {
if (e) {
console.log('error', e);
} else {
console.log('duration', this.tickSound.getDuration());
}
});
this.alarmSound = new Sound('alarm_clock_sound_short.mp3', Sound.MAIN_BUNDLE, (e) => {
if (e) {
console.log('error', e);
} else {
console.log('duration', this.alarmSound.getDuration());
}
});
// stopされるまで繰り返す
this.tickSound.setNumberOfLoops(-1)
// 再生
this.tickSound.play()
完成したサンプルアプリはこちらにアップしています。
sohta3/Pomodoro
ios
まとめ
React+ReduxなネイティブアプリをReact Nativeでシュッと作ってみました。
React Nativeアプリをスピーディーに開発するには、今回やったようにReactで書かれたWebアプリをReact Native化する開発フローが有効ではないかと考えています。

今回のサンプルアプリではUIがかなり手抜きとなってしまいましたが、WebアプリとNativeアプリのコアロジック(アプリケーション層・ドメイン層)をJavaScriptコードで共有できたことから、React Nativeの強力さを垣間見ることができました。
また、npmで「react-native」のキーワードで検索すると2,018件のライブラリがヒットします(2016/6/26時点)。

React Nativeのエコシステムはなかなか成長できていると言えるのではないでしょうか(ちなみにnativescriptは270件、cordovaは2,504件でした)。
npm
今後もReact Nativeの可能性を追っていきたいと思います!
それでは!