TL;DR
iOSDC Japan 2017 というイベントが先日開催され、そこでReact Nativeを題材に何かトークをしようと応募したら、まんまと枠をもらえたので話をしてきました。
事前のリサーチでiOSのエンジニアはReactやReact Nativeについて名前や概要ぐらいは知っているけど使ったことのある人は多くないということが分ったので、まずは興味を持ってReact Nativeの環境をインストールしてもらうことをゴールに、Reactプログラミングのパラダイムや思想について、iOSエンジニアが普段行なっているネイティブアプリ開発とどのように違うのか? という内容にしました。
いくぶん抽象的な話に終始してしまい力量不足で本当はもっとわかりやすい説明をしたかったなと反省しつつも、実践的な内容はこれからも各所で見られることになると思うので是非React Nativeにご注目ください。
*
以下は当日に上映したスライドに、スピーカーノートして書いたテキストを交えたものになります。資料だけでは情報が不足していると思われるのでご参考ください。
このトークのテーマは「Viewをどう作るか?」という大きな問題に対して、React Nativeが提供するプログラミングモデルをiOSアプリ開発者の視点でお話します
前提にあるのは「Viewの開発は難しい」という問題です。
Viewはユーザーにも開発者にも心理的な距離が近いので、現実のアプリケーションを作る上で「Viewをどう作るか?」というのは重要な点になっています
その中でFacebookは20億人とも言われるユーザーに対して日々Viewを開発し続けている、世界最大のViewの生産者であると言えます。 そんなFacebookの開発の現場で生まれたUI開発のための考え方を紹介したいと思います。
思い出したように突然の自己紹介です。
私は普段はSwiftとKotlinでiOSとAndroidのネイティブアプリケーションを開発しているのですが、趣味でアプリケーションフレームワークを研究している者です。
まず始めにReact Nativeを一言で説明すると「React でiOSやAndroidのネイティブアプリケーションが開発できるツール群」ということになります
Reactはもともとブラウザ向けのViewを開発するライブラリとして登場しましたが、ブラウザとそれ以外のプラットフォームにViewの解釈が拡大してからは、フレームワークやUIプログラミングのためのプラットフォームの側面が強くなっています。
早速React Nativeを使ったViewのコードをお見せします。
ReactではReact Componentという単位でViewを開発します。
React Componentの考え方はUIViewControllerに近いので、あえてViewControllerという名前をつけました
Interface BuilderやStoryboardは利用せず、Viewの定義はすべて render() 関数以下に埋め込みます。
これはUIKitでいうところの loadview() メソッドになるでしょう
React Native がViewを書き出すまでの仕組みです
React Nativeで書いたJavaScriptのコードは、仮想的なViewのデータ構造としてバックグラウンドスレッドでJavaScriptCoreによって処理され、最終的に1つのルートUIViewとなって出力されます
このためネイティブアプリの中の一部分のView以下にReact Nativeを使うという方法も可能になっています
そして、私達がUIKitでViewをどのように作っているのか? というお話にふれます
UIKitでのプログラミングではInterface Buildを通じてViewControllerが扱う初期状態のViewを定義します
ViewController内部プロパティでプレゼンテーションのための状態データを管理し、イベントが発生するごとに操作してViewを書き換えるコードを書いています。
Viewを更新するコードの積み重ねは、1つのViewControllerが抱える要件が増えていくことによってどんどん肥大化していきます。
ここにViewの開発は難しいという問題の根本があると思っています。
これに対しReactコンポーネントはViewに必要な状態が変る度にView全体がフレームワークによって自動で再構成されます
開発者はViewを更新するための具体的な手順を記述せずに、Viewが描画された後の結果を直接XMLのコードで表現します
これを宣言的なViewと呼びます
Reactの宣言的なViewの利点は、フレームワークによって「Viewをどう表示するのか」という手続きが隠され、「どう表示された」だけを抽象的に考えてViewを作ることができます。
Interface Build が表示する前の状態を定義するのとは対照的に、ReactのViewは表示するViewそのものをコードで表現します。
この為、状態変数によってViewの内容が変わる、というコードを render() 関数内にすべて書くことができます。
これは継続的なUI開発で変更しやすく複雑になりにくいコードを維持することにつながります。
次に複数のReact Componentを使ったコードを見ていきたいと思います
React NativeではUIKitの各コンポーネントはReact Componentとしてフレームワーク内に用意されているので、直接UIViewControllerなどを操作することはありません
開発者は独自のReact Componentを定義して、それらを組立ながら画面を構成していきます。
これをReactではコンポーネント指向なViewといいます
コンポーネント間のデータのやりとりは一般的には、より上位のコンポーネントから差しこんだり、ハンドラ関数を登録し任意のコンポーネント内で処理が実行されるようにしたりします(Delegateパターンに少し似ています)
コンポーネント指向でViewを構成することの利点は、各Viewが小さな部品となり取り扱いやすくなることにあります。
一般的なプログラミングでクラスや関数を作ることと動機は同じです。
コンポーネントはただのデータ構造なので、それ自体が引数と戻り値がある関数であるととらえることができテストも書きやすいです
大きくなったコンポーネント下位コンポーネントへ分割し小さくしたり、内部の詳細はカプセル化されているので他のコンポーネントと共有化することもできます
またUIViewControllerモデルではどうしてもViewControllerの切り替え=画面遷移して移動。となってしまう場面でもいつの画面内で済ませられるように感じます
React Component と UIViewController を比較した時に UIViewController側に不足しているのは複数のコンポーネントを組み合わせる時の協調性です
各ViewとViewControllerがどのような親子関係にあるのかをaddChildViewController() で記述することになります。
Reactのような自動でViewを描画する仕組みもないので、データが更新された時に適切に内部通知されデータが渡される仕組みを作り、開発者が手動で必要なViewを探し更新する、もしくはデータバインディング系のライブラリで補うことになります
ReactはどうViewを作るのか? についてまとめます
ReactのViewは内部状態の更新以降の処理をライブラリ側で行ってくれます
命令ではなく結果をコードで記述するようになるので、よりアプリの本質的な部分だけを考えることができます
ReactのViewはコンポーネント単位の環境に依存しないデータ構造になります
小さな部品を組み立てるようにViewを構成するので複雑なUIをより開発しやすくします
これに対しUIKitプログラミングでは、Interface BuildとViewControllerを駆使してViewを作ります
基本的にViewcontrollerを起点に開発者がすべてViewを手動で記述するので、Viewが複雑になりがちです
React Nativeのすごいところは、UIKitプログラミングの考え方とは全く異るパラダイムで同じアプリケーションを開発できるという体験を持ち込むことに成功しています
UIKitをそのまま動かした上で、既存の開発環境の抱える問題をうまく解決しているという点が非常に夢がある技術だと感じます(しかし現実は厳しい事もたくさんあります)
Interface Builder で構成するViewは状態管理に弱く、命令的なコードを開発者自身が記述しないといけないため、Viewの複雑さを引き起します
また、1画面すべてをViewController以下に収めるプログラミングは拡張性に乏しく、柔軟性に欠けます
React Nativeが解決する問題は現在のUIKitの制約と深く結びついています
つまりiOS SDKやXcodeの進化によって改善するべき点を示唆していると思います
最後にこれらの問題を解消すべく、UIKitがどう進化するといいのか? という話題について個人的な妄想を話します
宣言的なViewを実現する方法としてUIデータバインディングがあります。
MacアプリにはCocoaバインディングというInterface Builderでネイティブに動作するデータバインディングの仕組みが既にあります。
これを是非UIKitプログラミングの世界にも統合して使えるようにしてほしいです
さらに次世代のInterface Buildでは、WindowsやAndroidの開発環境のようにプレーンテキストとプレビュー形式でUIを構成したいです
もしくは、コンパイラレベルの拡張が必要だと思いますが、JSXのようにSwift内でViewの構造を宣言できるのも面白いかもしれません
またReactのコンポーネント指向を例に、複数のUIViewControllerが協調して画面を構成できる仕組みを強化して欲しいところです
Appleらしいやり方としてはStoryboardを拡張してグラフィカルに記述できるようになるというのもいいかもしれません
その時はReactのFluxアーキテクチャのようなコンポーネントツリーを横断してデータを取得する方法の研究も必要になってくると思います
皆さんもReact Nativeで「未来のiOSプログラミング」を想像(創造)しませんか?
以上で私の発表は終了させていただきます
おまけ
尺の関係で個人的には好きだが技術的な解説に寄ってしまうので削った部分がありました、別の機会で何か残したいと思います
- React DOM
- React + (View)
- View=DOM, Native, Canvas, SVG, WebGL
- ^ 一方ウェブブラウザ上で実行されるReactはReact DOMと呼ばれています。
- Reactのシステムによって最終的にViewが描画されるプラットフォームごとにパッケージ化されており、そのうちの1つがReact Native となります
- React for iOS を実現するツール
- AsyncDisplayKit
- ComponentKit
- React Native
- ^ React Native 以前にReactにヒントを得てiOSのUIKitで動作するライブラリとして「AsyncDisplayKit」と「ComponentKit」が存在します。
- Facebookの社内プロジェクトとして開発は独立していて、それぞれ発展を遂げているのですが、現在のReact Nativeに影響を与えています。
- React Nativeの仕組み
- JavaScriptコードをバンドル
- RNランタイムをロード
- バックグラウンドと通信してRootViewが更新される
- ^ React NativeではコンポーネントのレイヤーをJavaScriptで記述し、JavaScriptCore経由でネイティブのUIKitコンポーネントのViewへ適応します。
- 前述のように内部的には各Viewに相当するデータ構造を保持していまして、バックグラウンドスレッドや最小限のコストで描画処理が走るよう最適化されています。
- Virtual DOM or Virtual View
- Viewのデータ構造を生成し
- 差分を部分的に描画する
- 高速なViewの描画が可能になる