Skip to content

Instantly share code, notes, and snippets.

@atsushieno
Last active June 6, 2019 11:20
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atsushieno/3d5a8ea2c4b1fd670d37ff55f747384e to your computer and use it in GitHub Desktop.
Save atsushieno/3d5a8ea2c4b1fd670d37ff55f747384e to your computer and use it in GitHub Desktop.

Google I/OでAndroid向けの新しいUIライブラリとしてJetpack Composeがアナウンスされ、その翌月にはApple WWDCでSwiftUIがアナウンスされ、GUIフレームワークには確実に新しい流れが来ている。Flutterについても同じようなトレンドに乗っているということができる。

これらは、「トレンド」として捉えれば妥当だが、「革新的」だったわけではない。これからいくらでも宣伝過剰な記事が出てくると思うので、なぜこれらが革新的ではないのかを説明することを主眼に置いて、これまでのGUI開発において起こってきたことをなぞりつつまとめておきたい。革新的でないことを立証するのは過去の実例を出せばいいだけの話なので簡単だ。

Flutter、Jetpack Compose、SwiftUIに共通する特徴を挙げてみよう。

  • 特定の言語(Dart/Kotlin/Swift)に依存したフレームワークの最適化
  • リアクティブな設計に基づく宣言的UI (Streams, Combine frameworks)
  • インスタントなUIの反映を意識した設計(Hot Reload, Apply Changes, Xcode Swift REPL)
  • プラットフォームの古いUIツールキットに依存しない描画レイヤーからのフルスクラッチ設計

Jetpack ComposeとSwiftUIについては次の共通点もある。

  • ネイティブUIファーストの復権

特定の言語

UIフレームワークが特定の言語に合わせて設計・強化されるという側面もあるが、言語のほうが新しいUIフレームワークの発展に合わせて機能強化されるというトレンドがある。

Flutter

Dart 2.0がそもそも全面的にFlutterのために新機能を追加したもの

Jetpack Compose

  • TODO: Kotlin最新版の新機能を確認
  • コレクションの記述など?
  • Kotlinを活用したイベントの記述は非常にシンプル。同じことはJavaでは到底実現できないし、C#でも怪しい。イベントなどはobject initializerで簡単に記述できない。

SwiftUI

UI as codeのメリットとそうでないもの

  • UI as codeの実現
  • 開発環境の単純化・コード分析の一本化
    • XMLの表現力は貧弱なので、Androidのdata-bindingのようなテンプレート言語が入ってくることになって、結局そのためのリソース(学習時間や解析系実装コスト)が無駄に費やされることになる
    • 反復処理やささいな条件分岐などコードで書けば瞬殺で終わるものがXMLで無駄に面倒になる
    • 言語ツールが単なるコンパイラだけではなくlanguage service的な機能を統合できたことによって可能になった側面が多分それなりにあるかもしれない。現状あまり関係ない気がしている。
      • Jetpack Composeについては正直よくわからない。Apply ChangesはAST分析などを伴わないし、annotationの付いたコードは主にUIコード生成に使われている雰囲気があるので、コード分析は全くいらない気がする
      • SwiftはREPLが強い。ただこれ自体はlanguage serviceが実現しているわけではない
      • Flutter/Dartはもともとインタープリターでコードの部分的実行を実現できているのではないか(Dart処理系は正直よくわからん)
  • UIをXMLで記述するフレームワークでもHot Reloadのような機能は実現できていた(LiveXAMLなど)
    • リソースをまるっと入れ替えてActivity等をrestartするだけなので実行の仕組みさえあれば全然難しくない

同じことを実現している・実現できそうな言語

  • ElmやF#のElmish

リアクティブな宣言的UI

  • リアクティブな設計 ≒ 宣言的にイベントを記述できるモデル
  • 宣言的UI: テストが容易になる
  • MV-whateverの実現(BLoC, AAC ViewModel, )

この辺はフレームワーク側が開発モデルを用意するところまで来たという程度で、実のところフレームワーク側がやる必要があったとは言い難い。どのフレームワークも最初はフレームワークの外側からReactiveを実現していた。

インスタントなUIの反映

「Hot Reloadができること」を目標にしたり、コレでUIフレームワークの出来の良し悪しを判断するのはバカバカしい。目標は「アプリケーションのUIを手早く構築・確認できること」にあるはずだ。目標が実現できるなら手段は何でもよい。

いくつかのアプローチがある。

  • 疑似再現環境におけるUIの部分的実現 - Xamarin Inspectorなど
  • REPLによる実現 - SwiftUI, Continuous Coding
  • (主に)UIコードの差分(部分的)実行による実現 - Flutter Hot Reload, LiveXAML (, Xamarin LiveReload), Apply Changes, Instant Run warm swap, LayoutCast

Inspector、Previwerの類は実のところHTML等を使用してUI部品を擬似的に実現したものが多く(XamarinもFlutter も)、再現度には限界がある。

LiveXAMLやLayoutCastはその手段においてXMLリソースの更新とアプリケーションの部分的再実行(Activityの再起動など)を行っているのであって、本質的にはFlutterのHot Reloadと何ら変わらない。

Continuous CodingにおいてはXamarinアプリケーションにおけるコードのREPL実行を(UI、非UIを問わず)実現している。

ただUIはActivityの再起動など所定のマナーに基づいて行われるべきであり、そのための実行基盤が整備されているべき。これを整備して「ビューのコードではStateとWidgetを切り離せ」と明示したのはFlutter Hot Reloadの特性であり、その他の機構(Continuous Coding, Instant Runなど)の問題といえる。当然ながらビューとロジックの切り離しという発想は20世紀からある。Apply Changesは処理内容が明確化されており、この特性を別のかたちで実現しているといえる。

インスタントなコード実行を可能にする・妨げる要因:

  • アプリケーション・パッケージに含まれないコードの追加実行可否: iOSデバイスの動的ネイティブコード実行の禁止やAndroidで実行中に差し替えられないようなのDalvikバイトコードの更新(クラスの追加など)が問題になる
  • プラットフォーム コードでないものは実行が比較的容易(React Native, Dart, mono)
    • ただしプラットフォーム コードの変更を伴う場合は別(XamarinでACWクラスのメンバー追加を伴う場合など)
    • 単にコード実行方式がインスタント実行を容易にしている側面が強く、要は都合が良かっただけであって、これらが可能になっている言語が特別に優れているわけではない

描画レイヤーからのフルスクラッチ設計

  • いろいろ古い前提で作られたレガシーAPIを切り捨てることができる
    • iOS: 32bitの切り捨て。Metal前提
    • Android: armeabiの切り捨て (Vulkanは前提にできるかもしれないし、できないかもしれない)
  • Jetpack Compose(Android)については、GUIフレームワークに新機能を追加する際のプラットフォーム バージョン依存性を排除できる
  • SwiftUIの場合はむしろ新しいフレームワークが新しいOSに依存するので状況は悪化している(過渡的な問題ではある)
@kekyo
Copy link

kekyo commented Jun 5, 2019

UI as codeについて思うのは、XML的なアプローチがIDEの補助をそれほど得られなかったというのが私的に大きいと感じました。XAMLでもintellisenseはC#のそれとは比較にならず、当初の説得力だったコードで表現できない(listboxのitem templateのような)要素も、時代が進んだら遅延評価関数が簡単に書けるようになって、なんでこんな面倒くさいものを書かなきゃならないのか、という思いが強くなり... ましてやデザイナー(人間)との協業なんてどこかに吹き飛んで、今やデザイナーがフレームワーク知ってないとお話しにならないという状況...

@atsushieno
Copy link
Author

そうそう、結局複数言語にわたってサポートしなければならない状況になってIDEサポートの開発負荷が高かっただけなんですよね。もっとも、そう言えるようになったのは、プログラム言語側のサポートが十分に拡充されてコモディティ化してきたからこそかな、とは思っています。

デザイナーとの協業を可能にするにはデザインツールからプログラマー向けにデータを卸すという作業フローでないと厳しそうだなと思います。プログラマーと違ってデザイナーがデータを好き放題加工するのは無理があるので…

@kekyo
Copy link

kekyo commented Jun 6, 2019

あと、宣言的UIの表現力の限界もありました。宣言的UIが一度崩される(やりたい表現に対して能動的なコードを記述せざるを得なくなる)とコードが意図も簡単にグチャグチャになります。rxの演算子が(人によっては覚えるのが大変なぐらい)沢山あって、受動的なコードを書くにはこれでもまだ足りないと思う動機になっているような気がします。ここが遠因して、このコードはVMに書くべきかMに書くべきか、あるいは人によってその解釈が分かれるというような事を起こしているんじゃないかとも。(はじめに思ったのはXAMLではなく、WiXだったりする。私的には、全部宣言的に記述できるのは理想だけど、まあ無理じゃないですか、という思い)

@atsushieno
Copy link
Author

そうなりそう…まあFlutterでも同じことが起こっていておかしくないはずなのですが(Stateに隔離し忘れたりとか)、あまりそういう失敗にハマっている話を見ることがないので、「WidgetとStateを分けろ」程度のガイドラインだけでも割となんとかなるのかもしれないな、とも思っています。「XAMLで書けることだけ」に隔離する人間不信的アプローチ(?)も、この意味では合理的な…といっていいのかな…側面はありますね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment