🐦

Shorebirdが開発するFlutter用の『Code Push』とは

2023/07/02に公開

はじめに

Shorebird は Flutter 開発でビジネスを成功できるように、必要な製品を開発するパブリックな組織です。プロダクトはすべてオープンソースであり、開発の状況も Discord で公開されています。チームには、Flutter 創設者の Eric Seidel 氏はじめ、Bloc や Mason の作者でもある Felix Angelov 氏など、言わずと知れた Flutter のエキスパートによって構成されています。
そして、現在プロダクトとして公開されているのが「Code Push」となります。今後は他にもたくさんプロダクトを開発するのだと思いますが、執筆時点で公式サイトに掲載されているのは Code Push のみです(GitHub を見ると他にも開発していることがわかります)。

Code Push は、アプリストアを経由せずにユーザーの手元にインストールされたアプリを更新できるツールです。例えば「アップデート直後に致命的なバグが潜んでいたことに気づき、なるべく早く修正したい」といった場面において、本来ならばアプリストアへデリバリ後に審査承認を経てユーザーへ配信されますが、これを使うと即時にバグ修正(パッチ適用)を全ユーザーに適用できます。
https://shorebird.dev/

Flutter とは独立したプロジェクトですが、彼らが立ち上げたプロダクトというだけで Flutter 開発者としては目が話せませんね。かく言う私も興味の範囲内で、GitHub や Discord など開発の進捗やディスカッションの様子を不定期に覗いていました。

これまでツールの利用には Team プラン月額20ドルが必要で、まだ安定版ではないプロダクトにお金を出すのを躊躇っていましたが、2023年6月30日に待望の Free tier(Hobby という無料プラン)が登場 し、気軽に試せるようになりました。

今回は、表層的ですが実際に動かしてみたのでその手触りと、Code Push の概要、利用における注意事項などを記載します。

Code Push とは

Code Push はいわゆる OTA(Over The Air)と呼ばれるアップデートを実現するツールです。
OTA は、iOS/Android でもしばしば耳にする機会がありますが技術自体は古くからある概念で、IoT 機器のソフトウェアのアップデートなど、いわゆるネットワークに接続された状態でワイヤレスにアップデートを行う技術の総称です。用途としては、デバイスやシステムの機能改善やバグ修正、セキュリティの向上など、さまざまな目的で使用されます。

iOS/Android アプリ界隈では React Native でよく使われる Expo をはじめ、 Microsoft 製の React Native Module for CodePushAppCenter)などが存在します。

また、新しいバージョンをリリースせずにアプリを更新する別のアプローチとして Firebase Remote ConfigLaunch Darky がありますが、これらはあくまで設定値の更新であるのに対し、Code Push は既存のソースコードを書き換えたり新しいコードを追加できるため、明確な違いがあります。

こちらの記事でアプリ審査なしでのアップデート方法の比較がされており、わかりやすいです。
https://gaprot.jp/2021/09/29/app_store_review/

Flutter における Code Push の背景

2018年、Eric Seidel がまだ Google に在籍していた頃、彼自身で起票した Code Push に関するイシューがあります。
https://github.com/flutter/flutter/issues/14330

当時から様々な議論が行われていた様子が見られますが、ご存知の通り Flutter 公式で相応のツールは今でも存在しません。理由は、2019年4月23日にポストされた こちらのコメント に記載されています。3点の理由があげられています。

  1. パフォーマンス上の懸念

    • Android/iOS のストアポリシーに準拠するためには JIT コンパイルにせざるを得ない。技術的に採用は可能だが、パフォーマンスの低下につながり品質の低下を防げない(遅すぎる)。
  2. セキュリティ上の懸念

    • パッチ適用は基本的に任意のコードを実行可能にするので、マルウェアの潜むリスクが高い。署名である程度カバーできるだろうが、ミスが起こりやすくそれはの影響は深刻である。
  3. ホスティングサーバーが無い

    • アプリケーションにパッチを当てるためのオープンソースのホスティングソリューションが無い。そのため、開発者が独自に Web サーバを立ち上げる必要があるか、あるいはサードパーティツールを利用するかなどが必要である。

また、当時は Flutter 1.0の安定版リリースに向けて他に優先するべき開発がたくさんあったので、時期的な面でも逆風ではありました。

そういう意味では、Shorebird で開発しているこのプロダクトは、Eric Seidel が5年前から抱いていた野望を実現する悲願でもあるのかもしれません。そして、無事に開発を進めているということは、これらの懸念を払拭できた or できる見込みがあるということなのでしょう。

ユースケース

以下に主なユースケースが記載されています。shorebird patch というコマンド名からも分かりますがあくまで「パッチ適用」の範囲で使用するのが想定されるユースケースです。不具合修正などが最たる例ですね。よくアプリストアで「軽微な不具合を修正しました」のみの更新内容をみかけますが、このアップデートなどが主な対象になると理解しています。

  • 商用アプリの緊急修正
  • 古いバージョンのアプリを使用しているユーザーへの不具合修正
  • 定時配信(1時間ごとなど)

https://docs.shorebird.dev/faq#what-can-i-use-shorebird-code-push-for

Note that most app stores prohibit shipping code that changes the behavior of the app in a significant way.

そして重要な一文がこの内容で、機能追加などの大幅なコードの変更はアプリストアのガイドラインで禁止されていますので注意してください。詳細は アプリストアのガイドラインに抵触しないのか にて言及しています。

CLIで動かしてみる

開発ドキュメントはこちらです。利用するだけであれば躓きポイントは特にないので、ドキュメント通りにセットアップすれば完了します(Shorebird のアカウント登録を済ませて、shorebird init する)。
https://docs.shorebird.dev/

実際に手元で動かしてみた動画です。カラースキームの seedColordeepPurple から deepOrange に書き換えると、アプリ側に変更が反映されています。一見、ソースコードと端末が隣にあるので普段の開発と変わり無いように見えますが、一度ユーザーに配信されたアプリに対して変更が反映されるというのが肝です。
ちなみに、Background でパッチをダウンロードして適用する仕組みになっているため、一度アプリを立ち上げてタスクキルする必要があります。
https://twitter.com/h_tsuruo/status/1674641302363975680

アプリストアのガイドラインに抵触しないのか

アプリ開発者として「ストアを経由せずにアップデートする」と聞いてまず疑問に思うのが、App Store / Google Play のガイドラインに抵触しないのか、という点ではないでしょうか。
まずは、既にに広く利用されている Expo や CodePush がなぜ許容されているのかを Store Guideline Compliance で理解しつつ、FAQ を引用しながら改めてガイドラインを振り返ります。

先に結論だけお伝えすると、Shorebird 側の主張では「ポリシーに準拠して設計しているので問題ない」ですが、後述する課題をどう乗り越えている(乗り越えたのか)まではわかりませんでした。個人的にはまだグレーラインだと思うので、利用の際には自己責任でお願いします。

App Store

Apple Developer Program License Agreement(Japanese) の3.3.2項に記載があります。関連して3.3.3項も引用します。

3.3.2 インタープリター型コードは、アプリケーションにダウンロードできる場合がありますが、当該コードが、(a)App Storeに提出されたアプリケーションの予定および広告した目的に合致しない機能または機能性を提供してアプリケーションの主要な目的を変更せず、(b) 他のコードまたはアプリケーション用のストアもしくはストアフロントを作成せず、かつ、(c) 署名、サンドボックス、またはその他OSのセキュリティ機能を迂回しない場合に限ります。

これが、Expo や React Native の CodePush が許容されている理由です。インタプリタコードの JavaScript やアセットの OTA アップデートは完全に許容されており、2015年からずっとそのようです。正確には、ネイティブ SDK やシステム関数へのアクセスを提供しない場合に限り、WebView や JavaScriptCore で実行されるインタプリタコードの更新を許可しています。そのため、JavaScriptCore で実行される(v0.70からは独自開発の Hermes)React Native や、WebView で実行される Cordova や Capacitor では、それぞれ JavaScript コードに限定された API を提供するという形で OK とされてきたようです。

https://stackoverflow.com/a/43959434

3.3.3 Appleの書面による事前の承認なく、または第3.3.25条(In-App Purchase API)で許諾されているとおり、アプリケーションは、App Store、カスタムAppの配信、またはTestFlight以外の配布メカニズムにより、追加的な機能または機能性を提供し、解除し、または有効化してはならないこと。

ちなみに、これはアプリケーションの提供価値を大きく変える変更や機能追加がダメ(不具合修正などパッチ適用のみのユースケース)な理由です。

Google Play

Device and Network Abuse に配布に対する記載があります。

An app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. Likewise, an app may not download executable code (such as dex, JAR, .so files) from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine or an interpreter where either provides indirect access to Android APIs (such as JavaScript in a webview or browser).

最後の1文が重要で、Android API へのアクセスが制限されているコード(WebView やブラウザで実行される JavaScript)は制限の対象外となっていますね。

Dartでどのように解決するのか

要は、ネイティブ SDK にアクセスしない形でのインタプリタコードは許容される、という点が肝です。となると、リリースビルドで AOT コンパイルを基本とする Dart でこれをどう解決するのか、という点がやはり気になります(iOS の提供は遅れているのもおそらくこれが理由です)。前述の「Flutter における Code Push の背景」でも触れましたが、開発中止の理由にもなった JIT におけるパフォーマンス低下もどう乗り越えるかが大事なポイントになりそうです。

Dart VM や JIT/AOT コンパイルについては、昔に記事を書いていますのでご参照ください。
https://zenn.dev/tsuruo/articles/48909d22d49ffe#introduction-to-dart-vm

Discord では下記の質問に対して、Eric Seidel が回答しています。

– What are the contents of patches? Is it mainly AOT-compiled source? (I assume no raw JIT source)

AOT コンパイルされたバイナリをインタプリタ形式として使用し、それに含まれる ARM アセンブリ (のサブセット) を解釈する、と言及しています。いわゆる Dart の JIT コンパイルをそのまま使うのではなく、あくまで AOT コンパイル(パフォーマンスは維持したまま)、ポリシーに準拠できる形で工夫しているようです。

We’re in the process of modifying the Dart VM to support our code push on iOS, which includes modifying the AOT assembly generation, as well as teaching an the Dart VM and its ARM simulator to execute in parallel with ARM64 code on the real CPU, to allow replacing code at runtime without violating iOS technical restrictions around JITing.
https://shorebird.dev/jobs/

ちなみに、こちらにも記載されていましたが Dart VM 側の AOT アセンブリ生成周りを改変しているのは間違いなさそうです。ただ、Eric Seidel でカバーしきれない超低レイヤ部分のコンパイラエンジニアを高単価で探していたり、これに関してはやや苦戦(?)しているようです。

FAQには簡略な結論のみ

執筆現在、FAQ には「Microsoft や Expo などと同様にポリシーに準拠するよう設計されている。また、Code Push という技術自体は広く使われているから問題ないはずだ」といった記載で、結論だけ簡略に記載されております。ガイドラインの遵守については、今後一般公開が近づくにつれて Shorebird の FAQ で詳しい説明が記載されるそうなので、ひとまず本格的に利用するにはそのドキュメントが揃うまで待ったほうが良さそうです。

まとめ

今回は注目の Shorebird が提供するプロダクト、 Code Push について紹介しました。
本記事でもユースケースやガイドラインに触れましたが、あくまでもパッチ適用(緊急の不具合修正)用途に限定して、機能開発などは従来通りアプリストア経由での配信にするのが想定される活用方法だと思います。当面は iOS/Android のモバイル中心となり、ストアガイドラインとの兼ね合いで最大限活用しづらい部分はあるかもしれませんが、そこは Flutter のクロスプラットフォーム性能をいかんなく発揮して、Linux や組み込み系まで更新できるようになると幅が広がりそうです。

また、Release Notes を見ていただくとわかるのですが、2023年6月だけで11回のリリースをしているなどかなり早いスピードで開発が進んでおり、本記事の内容もすぐに古いものになる可能性が高いです。大きな更新があった際にはできるだけ追従していきたいですが、正確な情報を得るには記事内で引用している公式ドキュメントをご参照ください(本記事はあくまで導入の想定)。

今後の機能開発については以下に記載されていますが、これらがすべて実装されるとかなり使い勝手良くなりそうで楽しみです。まずは2023年7月に対応予定と言及されている iOS 対応が直近のマイルストーンですね。
https://docs.shorebird.dev/status#what-doesnt-work-yet

iOS でのデモも既に YouTube に公開されており、周辺の整備をしている段階らしいのでもう間もなくでしょう。

https://www.youtube.com/watch?v=7KDgFvdogsE

まだ小さかったプロダクトがどんどんと進化して認知されている様子を見ると非常にワクワクしますね。Discord で開発の様子もパブリックにされてますし、その作業風景を見るだけで非常に勉強になるので一度覗いてみてはいかがでしょうか。

参考

Discussion