目次


React Native: 迅速な iOS 開発の新しい世界へ

強力なツール・サポートを利用して JavaScript で iOS ネイティブ・アプリを作成する

Comments

驚異的な成功を収めた React JavaScript オープンソース UI ライブラリーのリリースから 2 年経った 2015年 3月に、Facebook/Instagram は F8 カンファレンスでさらなるセンセーションを巻き起こしました。React の前身を確固として支持する開発者を含め、React を称賛する開発者たちからなるグローバル・コミュニティーを大喜びさせたことに、React Native プロジェクトがオープンソースとして GitHub で公開されたのです。それからわずか 3 ヶ月のうちに、React Native は 170 人を超えるグローバル・コントリビューターのサポートを得て、15,000 を超える数のスターを獲得しました。どの驚異的な機能が、ここまで熱烈な歓迎を受ける理由となっているのでしょう?一言でいうと、それは、iOS アプリを JavaScript でとても簡単に開発できることです。React Native はこれ以外にもさまざまなことを実現することから、iOS が登場して以来、最も急成長を遂げている開発プラットフォーム兼ツールとなっています。

このチュートリアルでは React Native の概要を紹介し、そのアーキテクチャーと内部動作を説明します。その後、重要な React Native 開発手法に焦点を当てた完全なサンプル・アプリに取り組みます。そして、React Native で利用できるツールの概略についても説明します。React Native の本質を理解するには、React プログラミングの知識が必要です。このチュートリアルでは、読者がその知識を持っていることを前提とします。意欲のある JavaScript 開発者なら、半日もあれば両方のライブラリーについてすぐに学習することができます。それとは対照的に、従来型の iOS 開発を習得するには通常、何ヶ月もかかります。由緒ある Objective-C (または、それよりも新しい Swift) プログラミング言語を使いこなせるようになるまでに何週間もかかり、iOS 特有のレイアウトと UI を作成する手法とツールを理解し、複雑な Xcode IDE の細かくて微妙な部分をマスターし、多面的なビルド・ワークフローを熟知しなければならないからです。

必ず、私が作成したチュートリアル「React Native と Advanced Mobile Access を使って iOS 8 用ゲームを作成する」を読んでください。IBM Bluemix の MobileFirst プラットフォームが提供するリッチなサービスを使用すると、いかに簡単に React Native アプリを機能強化できるかがわかります。

React Native の内幕

React Native は、React (すでに何万人もの忠実な JavaScript 開発者たちが使用している、成熟したライブラリー) の最も優れた機能をモバイル開発に適応させています。React では、再利用可能なハイパフォーマンスのコンポーネントを組み合わせてアプリを構築したり、これらのリーン (効率的な) コンポーネントを作成したりすることができます。React はパフォーマンスを最適化するために、Web 開発者をブラウザーの難解な DOM から切り離します。そのために使用しているのが、仮想 DOM の概念です。React コードで仮想 DOM を変更すると、React ランタイムが物理 DOM のリフレッシュと更新の最適化を引き受けてくれます。

React Native のアーキテクチャー

React Native でのプログラミングも、仮想 DOM を変更するという方法で行います。ただし React の場合とは異なり、React Native の仮想 DOM は、物理ブラウザーではサポートされません。仮想 DOM がターゲットとしてレンダリングするのは、正真正銘の iOS ネイティブ UI コンポーネントからなる出力です。

こうした魔法のすべては、最適化された非同期ブリッジによって実現します。図 1 に、React Native のランタイム・アーキテクチャーを示します。

図 1. React Native のランタイム・アーキテクチャー概要
React Native のランタイム・アーキテクチャーを示す図
React Native のランタイム・アーキテクチャーを示す図

この柔軟なアーキテクチャーでは、JavaScript インタープリターをどこででも実行することができます。通常、インタープリターは端末またはエミュレーター上のネイティブ・アプリ内部のスレッド上で実行されますが、リモートからデバッグする際は、JavaScript コードを開発者のブラウザー内で実行することができます (「強力なツール、即座に得られる満足」のセクションを参照)。画像のデコード、ディスク I/O、レイアウトといった時間のかかるタスクも、他のワーカー・スレッドに任せることができるため、最近のモバイル端末に搭載されたマルチコア CPU による並列処理を最大限利用できるというわけです。

iOS プラットフォームのネイティブ・コンポーネントのレンダリング

React Native のプログラミング・モデルでは、ブラウザー内でコードを実行していないという事実をできる限り隠しています。しかしながら、標準的な HTML コンポーネントのプロパティーと属性のなかには、iOS プラットフォームのネイティブ UI コンポーネントでは使用できないものが数多くあります (その逆も然りです)。React Native でのプログラミングで使用できるのは、標準的な HTML コンポーネントの標準的な属性の一部に限られます (詳細については、React Native の API ドキュメントを参照してください)。

DOM コンポーネントとは異なり、iOS の UI コンポーネントは CSS のスタイル設定に対応しません。このことから React Native では、CSS ではなく JavaScript の StyleSheet オブジェクトを使用して、すべてのスタイル設定を適用します。ただし、React Native は、お馴染みの CSS 属性セレクターのサブセットを提供する、お馴染みの CSS に似た構文をサポートしています。

iOS アプリでのユーザー入力イベントの処理は、標準的な Web アプリでの処理とは大幅に異なります。React Native には、iOS 独自のシステムを基にモデル化された、包括的なジェスチャー・レスポンダー・システムがあります。アプリで複数のコンポーネント層でのイベントの伝搬やカスタム・エスカレーションをきめ細かく制御する必要がある場合には、このジェスチャー・レスポンダーを微調整することが可能です。

最後になりますが、同じく重要な点として、Objective-C や Swift を使用する一般的なモバイル開発者は、カメラの制御からネットワーク・アクセス、そして連絡先電話番号の管理に至るまで、基礎となるプラットフォーム API の充実したサポートを利用することができます。React Native が、これらすべての機能をサポートしながらもリーンなプラットフォームであり続けるのは、おそらく不可能でしょう。そのため、React Native ではそのアーキテクチャーの中心にある非同期ブリッジによって、Objective-C による任意の API と JavaScript との間で双方向のエクスポートをサポートするようになっています。この設計によって、React Native チーム (ならびに、さらに大規模な React Native 開発者コミュニティー) がプラットフォームの機能と API を少しずつマイグレーションすることを可能にしているのです。

React Native サンプル・アプリのツアー

このチュートリアルのサンプルは、ドキュメントと動画のビューアー・アプリです。アプリを起動すると、初期画面にスクロール可能な記事のリストが表示されます (図 2 を参照)。

図 2. アプリでの選択可能な記事のリストの表示
サンプル・アプリの初期画面のスクリーンショット
サンプル・アプリの初期画面のスクリーンショット

記事を選択すると、その記事が Web ビューに表示されて、その内容を読むことができます (図 3 を参照)。

図 3. 読むために選択された記事
アプリの記事ビューのスクリーンショット
アプリの記事ビューのスクリーンショット

左上の「Articles (記事)」リンクにタッチすると、記事のリストに戻ることができます。視聴可能なオープンソースの動画リストを表示するには、「Videos (動画)」にタッチします (図 4 を参照)」。

図 4. アプリに表示された視聴可能な動画のリスト
視聴可能な動画のリストのスクリーンショット
視聴可能な動画のリストのスクリーンショット

いずれかの動画を選択すると、その動画が新しい動画ビューで再生されます (図 5 を参照)。

図 5. アプリで再生されたオープンソースの映画「Elephants Dream」

左上の「Videos (動画)」リンクにタッチすることで、いつでも動画の再生を停止することができます。動画の再生を停止すると、動画のリストを表示する画面に戻ります。

図 6 に、このアプリでのシーンのストーリーボード・フローを示します。記事を表示するメイン画面からは、動画のリストにナビゲートすることも、記事を選択して Web ビューにその記事を表示することもできます。動画のリストからは、再生を始める動画を選択することも、記事のリストに戻ることもできます。裏でストーリーボード・フローのオーケストレーターとなっているのは、<NavigationIOS>コンポーネントです (「<NavigatorIOS> コンポーネントによるビューのオーケストレーション」のセクションを参照)。

図 6. dwviewer アプリのストーリーボード・フロー
サンプル・アプリのストーリーボード・フロー図
サンプル・アプリのストーリーボード・フロー図

React Native のインストールとサンプル・アプリの実行

Apple では、すべての iOS 開発を Mac 上で行うことを義務付けています。iOS アプリに取り組むには、Mac にインストールされた最新バージョンの Xcode が必要です。このことは、React Native を使って作成する iOS アプリにも当てはまります。この「Getting Started」ガイドに記載されているすべての要件を満たす Mac を使用してください。また、このガイドに従って、以下のコマンドで React Native をインストールします。

npm install -g react-native-cli

プロジェクト・ディレクトリーで、以下のコマンドを実行します。

npm install

React Native には、骨組みとして実際に機能するボイラープレート・アプリを生成するジェネレーターが備わっているので、このジェネレーターによって生成されたボイラープレート・アプリをカスタマイズすることができます。このアプリには必要なすべてのツールが統合されています。したがって、アプリを実行するために必要な作業は、Xcode でアプリの .xcodeproj ファイルを開いて実行することだけです。Xcode がアプリをビルドして、PC とエミュレーターまたは端末との間のブリッジとなるパッケージング・サーバーを起動します。そして、iOS エミュレーター (または実際の端末上) でアプリを起動します。

チュートリアルのサンプル・コードをダウンロードしてください。そこに、完成した dwviewer アプリが含まれています。ルート・ディレクトリーで npm install を実行した後、次のコマンドを実行して Xcode プロジェクト・ファイルを開きます。

open dwviewer.xcodeproj

ファイルが開いたら Xcode で「run (実行)」をクリックして、プロジェクトをビルドし、シェルの中でパッケージング・サーバーを起動し、エミュレーター内でアプリを起動します。これで、図 2 に示したアプリの初期画面が表示されるはずです。

エミュレーター内で React Native アプリが実行中になったら、ホット・キーを押して、パッケージング・サーバーを介した JavaScript コードのリロードをトリガーすることができます (アプリを端末上で実行している場合は、端末をシェークすることで同じ効果が得られます)。パッケージング・サーバーは、定期的にプロジェクト・ディレクトリーをスキャンして、JavaScript ソース・コードの変更をチェックし、コーディング中にコンパイル・エラーが発生した場合はアラートを出します。Xcode のアプリ・プロファイラーが自動的に起動されるので、エミュレーター内でアプリが実行されている間は、アプリのメモリーおよび CPU に対する要求をモニターすることができます。

コードの詳細

サンプル・アプリのコードはすべて index.ios.js ファイル内にあります。補足的な datasource.js ファイルが、アプリにデータを提供します。

記事のリスト・セルのレイアウト

React ユーザーの目には、コードの構造は見慣れたものに映ると思います。JavaScript コードと併せて (囲み記事「ES6 JavaScript 構文」を参照)、JSX 構文を使用した render() 呼び出しがあることがわかるはずです。リスト 1 のコードが、単一の記事のリスト・セルを定義します。

リスト 1. 単一の記事のリスト・セルを定義するコード
renderArticle: function(article) {
  return (
    <TouchableOpacity
     activeOpacity={0.5}
     onPress={() => this.showArticle(article.url)}>
     <View style={styles.cellcontainer}>
       <View style={styles.celltitlerow}>
         <Text style={styles.title}>{article.title}</Text>
         <Text style={styles.year}>({article.year})</Text>
       </View>
       <Text style={styles.description}>{article.desc}</Text>
     </View>
    </TouchableOpacity>
    );
},

記事のリスト・セルは、<TouchableOpacity><View>、および <Text> という 3 つの要素で構成されます。

<TouchableOpacity> によるタッチ・イベントへの即時応答

レイアウトされたセルをラップする <TouchableOpacity> コンポーネントは、不透明度を変更するフィードバックを利用したアニメーションを提供します。このアニメーションは、ユーザーがそのセルにタッチした時点でネイティブ・コンポーネントが全体の処理を行います。このような即時フィードバックが、ネイティブ iOS アプリを、モバイル端末上で実行される Web アプリと差別化する要素となっています。activeOpacity プロパティーには、セルにタッチされたときに使用する不透明度 (完全な不透明の半分) を指定します。

<TouchableOpacity> コンポーネントは onPress ハンドラーを介してイベント (React 合成イベント) も転送します。このサンプル・アプリの場合、記事の URL を使用して ArticleView 固有の showArticle()メソッドが呼び出されます。React Native には、ラップされたコンポーネントにユーザーがタッチしたときの即時フィードバック・エフェクトをさらに詳細に制御するための <TouchableHighlight> コンポーネントもあります。

JavaScript の StyleSheet API と FlexBox を使用した、コンポーネントのスタイル設定

リスト 1 では、各種コンポーネントのスタイルを設定するために、style={styles.*} を使用していることに注目してください。React Native は、FlexBox レイアウトをサポートする JavaScript の StyleSheet API を使用します (囲み記事「FlexBox ポリフィル」を参照)。

リスト 2 のコードに、アプリのスタイルが定義されています。

リスト 2. スタイルを定義するコード
var styles = StyleSheet.create({
 ...
  cellcontainer: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
    backgroundColor: '#F5FCFF',
    borderWidth: 0.5,
    borderColor: '#d6d7da',
    padding: 5,
  },
  celltitlerow: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'stretch',
    backgroundColor: '#F5FCFF',
  },
...
});

リスト 2 ではお馴染みの CSS に似たセレクター (JavaScript との互換性のために、ハイフンで結んだセレクター名の代わりにキャメル・ケースが使用されます) と FlexBox レイアウト・オプションが使用されています。

<NavigatorIOS> コンポーネントによるビューのオーケストレーション

記事のセルにタッチされると showTouch() メソッドが呼び出され、<Webview> コンポーネントによって、その記事が表示されます。

showArticle: function(articleURL) {
    this.props.navigator.push({
      title: "Article",
      component: WebView,
       passProps: {url: articleURL},
    });
  },

<WebView> コンポーネントは、選択された記事を構成可能なミニブラウザー内に表示します。navigator プロパティーで参照されている <WebView> は、<NavigatorIOS> コンポーネントによって右からアニメーション化されます。lt;NavigatorIOS> は標準 iOS UIKit の UINavgationController のビュー・スタック管理 API をラップします。これは、昔ながらの iOS 開発者のほとんどが使い慣れている API です。<NavigatorIOS> には、ビューのスタックをプッシュ、ポップ、操作するためのさまざまな API があります。また、タイトル・バーにオプションで表示するタッチ・ボタンを介して前後のナビゲーションも提供します。<NavigationIOS> コンポーネントは、アプリのルート・コンポーネントです (リスト 3 を参照)。

リスト 3. <NavigationIOS> コンポーネントを作成するコード
var dwViewerApp = React.createClass({
...
render: function() {
  return (
    <NavigatorIOS ref='nav' style={styles.container}
      initialRoute={{
        component: ArticlesView,
        title: 'Articles',
        rightButtonTitle: 'Videos',
        onRightButtonPress: this.onButPress,
      }}
     />
    );
  }
});

initialRoute プロパティーが、表示する初期ビューを指定します。上記の場合、そのビューに該当するのは ArticlesView です。この initialRoute プロパティーには、ビューに表示するタイトルが含まれており、さらにはボタンのタイトルとタッチ・ハンドラーがオプションで含まれています。これらのボタン (左右のボタン) は、指定された場合にタイトル・バーに表示されます。<NavigationIOS>navigator プロパティーを、その直接の子すべてに渡します (この例では、ArticlesView に渡します)。これらの子は、この navigator プロパティーを使用して、管理対象のビューのスタックを操作することができます。

<ListView> コンポーネントの処理

記事のリストを表示する ArticlesView コンポーネントは、React Native の <ListView> を作成します (リスト 4 を参照)。

リスト 4. <ListView> を作成するコード
var ArticlesView = React.createClass({
...
render: function() {
    ...
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderArticle}
        style={styles.listView}
      />
    );
},

<ListView> はネイティブ UIKit の UITableView コンポーネント (ほぼすべての iOS ネイティブ・アプリでは、このコンポーネントを使ってリストを表示します) をラップしませんが、非同期ブリッジ操作で十分なパフォーマンスを維持できる、独自のバージョンを提供しています。

<ListView> がデータをロードするには、datasource プロパティーを使用します。このプロパティーは、上記では ArticleViewdataSource 状態変数として指定されています。renderRow プロパティーは renderArticle() メソッドに設定されています。このメソッドは、前に説明したように単一の記事セルをレンダリングします。

dataSource 状態変数は、データを静的にコーディングされた JSON オブジェクトの配列として定義している datasource.js から、<ListView> のデータをロードします。<ListView> のデータが取得されるのは、ビューが最初に表示されるときの 1 回だけです。データの取得は、ArticlesViewcomponentDidMount ハンドラーが処理します。

var DataSource = require('./datasource');
...
componentDidMount: function() {
  this.setState({
   dataSource:    this.state.dataSource.cloneWithRows(DataSource.getArticles()),
   loaded: true,
  });
  },

記事と動画の間のナビゲーション

ユーザーが右上の「Videos (動画)」ボタンにタッチすると、ArticleViewonButPress() メソッドが呼び出されます。このメソッドは <NavigatorIOS>push() メソッドを呼び出して、VideosView のインスタンスをスタックにプッシュします。これにより、動画のリストが右からスライドインします。

onButPress: function() {
    this.refs.nav.push({
      component: VideosView,
      title: "Videos",
  });
  },

VideosView が表示されている間、タイトル・バーには「Articles (記事)」ボタンが表示されます (図 4 を参照)。ユーザーはこのボタンにタッチすることで、現在のビューをスタックからポップし、記事のリストに戻ることができます。

動画リスト・セルのレイアウト設定

動画リスト・セルは、VideosViewrenderVideo メソッドにおいて React Native コンポーネントを作成することによってレイアウトが設定されます (リスト 5 を参照)。

リスト 5. 動画リスト・セルのレイアウトを設定するコード
renderVideo: function(video) {
  return (
    <TouchableOpacity
      activeOpacity={0.5}
      onPress={() => this.showVideo(video.title, video.url)}>
      <View style={styles.videoscontainer}>
        <Image source={{uri: video.icon}} style={styles.vicon} />
        <View style={styles.videocellcontainer}>
          <View style={styles.celltitlerow}>
            <Text style={styles.title}>{video.title}</Text>
            <Text style={styles.year}>({video.year})</Text>
          </View>
          <Text style={styles.description}>{video.desc}</Text>
         </View>
       </View>
       </TouchableOpacity>
    );
  },

このレイアウトは、記事のセルのレイアウトと同じように見えます。異なる点は、動画のアイコンを表示するための <Image> コンポーネントが追加されていることだけです。動画リストにオフラインでもアクセスできるようにするには、これらの動画のアイコン用の静的 PNG ファイルをアプリの Images.xcassets ファイルを介して、アプリのリソース・バンドルに追加する必要があります。

Images.xcassets を使って静的リソースをアプリに追加する方法については、React Native ドキュメントの「Adding Static Resources to your App using Images.xcassets」セクション (スクロールダウンすると表示されます) の説明を参照してください。

コミュニティー提供の React Native コンポーネントの統合

VideosViewListview をロードして動画の選択を処理する方法は ArticlesView の場合とまったく同じなので、皆さんが行う演習として説明を省きます。1 つの大きな違いは、実際の動画を表示する部分です。

<WebView> を使用して動画を表示するのではなく、このアプリでは react-native-video を使用しています。これは、コミュニティーのメンバー Brent Vatne 氏によって作成された、サード・パーティーの <Video> React Native コンポーネントです。このコンポーネントを皆さん独自のアプリに追加するには、Vatne 氏が詳しく説明している、Xcode プロジェクトへアプリをインストールするための詳細な手順 (スクロールダウンすると表示されます) に従ってください。皆さん独自のアプリを作成する際は、コミュニティーが GitHub に次々と提供している多数の React Native コンポーネントを詳しく調べることをお勧めします。

このアプリで使用しているサード・パーティーの <Video> コンポーネントは、VideoViewer でラップします (リスト 6 を参照)。

リスト 6. <Video> コンポーネントをラップするコード
var VideoViewer = React.createClass({
  render: function ()  {
    return(
      <View style={styles.vidcontainer}>
      <Video source={this.props.source}
       rate={1.0}                   
       volume={1.0}                
       muted={false}                
       paused={false}               
       resizeMode="stretch"           
       style={styles.video}
       />
       </View>
       );
  },

上記の VideoViewer は、VideosViewshowVideo() メソッドで動画が選択された時点で、ビュー・スタックにプッシュされます。

showVideo: function(title, url) {
    this.props.navigator.push({
      title: title,
      component: VideoViewer,
      passProps: {source: {uri: url}},
    });
  },

これで、アプリのコードの調査は終わりです。

強力なツール、即座に得られる満足

オープンソース・ライブラリーというだけでも大きな一歩ですが、React Native の魅力となっているのは、React Native と一緒にリリースされている実用的なツール・セットです。これらのツールは、JavaScript 開発者が使用できる、リッチで成熟した既存のツールを活用しています。

図 7 に、アプリ内の開発者用メニューを示します。このメニューは、エミュレーター内または実際の端末上で React Native アプリを実行中に、ホット・キーを使用するとポップアップ表示されます。このメニューで、アプリをリロードしたり、コードのプロファイル作成を開始したり、現在のレンダリング FPS (Frames Per Second: 1秒当たりのフレーム数) を測定したりすることができます。さらに、Chrome または Safari の組み込み JavaScript デバッガーでリモートから JavaScript コードをデバッグすることも可能です。リモート・デバッグを利用すれば、単一のステップで、現時点での変数値を検査または修正したり、ブレークポイントを設定したりするなど、さまざまな操作を行うことができます。

図 7. アプリ内の開発者用メニューが表示された、実行中のネイティブ・アプリ

要素の検査オプションでは、iOS ネイティブ・アプリの実行中に React Native コンポーネントのスタイルと属性を検査することができます。端末の画面にポップアップ表示されるディスプレイは、見慣れているブラウザー・ベースの要素検査のレイアウトと同様です。図 8 に、このアプリ内オーバーレイを示します。

図 8. ブラウザーのような動的要素検査をサポートするアプリ内オーバーレイ

画面上に表示される、このようなアプリ内の開発者支援機能は、Objective-C や Swift を使用している iiOS ネイティブ・アプリの開発者が待ち望んでいたにも関わらず、完全には経験できなかったものです。React Native を使用する JavaScript 開発者は、公式リリース 1.0 の遥か前から、これらの機能を活用することができます。

まとめ

App Store に公開する iOS アプリを作成することを夢見ていた JavaScript 開発者は、今、その夢を現実のものにすることができます。React Native は現在、JavaScript 開発者の React プログラミングのスキルを iOS に適用できるようにしています。近いうちに、Android にも適用できるようになるでしょう。充実したコミュニティーのサポートがあることから、この先もずっと、新しいコンポーネントや機能 API が次々と利用できるようになるのは確かです。React Native はすでに止められない勢いに乗っているので、私はこの先、これがメインストリームの iOS 開発プラットフォームとして選ばれると確信しています。React Native は、iOS モバイル・アプリの開発を今までになく楽しいものにしています。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Mobile development, Web development, Open source
ArticleID=1015351
ArticleTitle=React Native: 迅速な iOS 開発の新しい世界へ
publish-date=09242015