MacなしでFlutterのiOSリリースビルド — Ripleyが外した3つのロックとxcrossとの分担

はじめに

「Flutter は iOS も Android も1つのコードベースで書けるが、iOS のビルドには Mac が要る」——この但し書きは Flutter が出てからずっと外れていない。CI で Mac ランナーの時間単価が Linux の数倍になるのも、チームに1台だけ置いた mac mini が詰まるのも、すべてここが根っこにある。

2026年9月16日、その但し書きに正面から手を入れる OSS が公開された。Ripley(Apache-2.0、Go 製)だ。Linux(および WSL 2)上で Flutter の iOS アプリをビルドし、IPA に署名し、USB 接続した実機にインストールするところまでを macOS も Xcode も使わずに通す。r/FlutterDev のスレッドが伸びているが、リポジトリはまだ v0.1.0、公開翌日という段階である。

この記事では、Ripley が具体的に何を突破したのかを3つのロックに分解し、先行する同種ツール xcross との役割分担を整理したうえで、それでも Apple から逃げられない部分を確認する。

「Macが要る」は1つの問題ではなく3つだった

Flutter の iOS ビルドを Linux に持っていくのが難しいのは、Xcode という1つのブラックボックスがあるからではない。性質の違うロックが3つ直列に並んでいる。

1つ目は Dart AOT のコンパイラだ。 Flutter のリリースビルドは Dart コードを gen_snapshot で AOT スナップショットに落とすが、iOS 向け gen_snapshot はmacOS ホスト用のバイナリしか配布されていない。これが決定的で、後述する xcross が「debug(JIT)ビルド専用」と自ら宣言しているのもこの一点が理由である。Ripley はここを、自前でビルドしたクロス版 gen_snapshot を別リリースとして配ることで埋めた。

Cross gen_snapshot binaries for Linux x64 and arm64 hosts. Both emit ios-arm64 Mach-O AOT snapshots and are installed by ripley toolchain fetch. — gen_snapshot ios-arm64 (Dart 3.13.3) リリースノート

Linux ホストで動いて ios-arm64 の Mach-O AOT スナップショットを吐く。ripley toolchain fetch の1コマンドで入り、ビルドに使った Dart SDK チェックアウトのライセンス表記が各バイナリに添えられている。3つのロックのうち、他のツールが誰も外していなかったのがここだ。

2つ目はリソースコンパイラだ。 .xcassets を Assets.car に固める actool、Storyboard をコンパイルする ibtool は、どちらも Xcode に同梱されるクローズドなバイナリで、単体では持ち出せない。Ripley はこれをPure Go で再実装している(internal/ios/assetcatalog/、internal/ios/storyboard/)。地味だが、ここを迂回できないと「アイコンが出ない .app」しか作れない。

3つ目はリンク・署名・実機転送だ。 Mach-O のリンクは clang + LLD、署名は .p12 + mobileprovision かアドホック(zsign -a)か --no-sign を選べる。実機への転送は usbmux・lockdown・AFC・DDI マウンタまで Go で書かれており(internal/ios/idevice/)、ripley device run でインストールと起動、ripley device logs でシステムログのストリーミングまでできる。

この3層が揃って初めて、ripley build ios --ipa が Linux で完走する。--mode profile、--flavor、--obfuscate と --split-debug-info、dSYM 生成まで揃っているのは、開発用のおもちゃではなく出荷物を作るつもりで書かれていることの表れだろう。プラグイン側も CocoaPods(static / dynamic 両方)、SwiftPM、Dart Native Assets、Cargokit(flutter_rust_bridge)に対応し、LocalSend・Immich・Saber・Smooth App といった実在アプリと、Firebase / Google Maps / ML Kit を含む複雑な Pod グラフで検証したと README は書いている。

xcross との関係 — 競合ではなく分担

Linux から Flutter の iOS 開発をやるツールは Ripley が初めてではない。2026年7月から公開されている xcross(Dart 製、MIT)は、Windows / Linux ネイティブで動き(WSL すら要らない)、実機でホットリロードまで回る。Swift の世界には xtool(5,400 スター超)があるが、こちらは SwiftPM パッケージを iOS アプリにするツールで Flutter は守備範囲外だ。

Flutter に限ると、Ripley と xcross はきれいに補完関係にある。

xcrossRipley
ビルドモードdebug(JIT)のみ。release/AOT は macOS 必須と明記release / profile の AOT を Linux で生成
ホットリロード◯(r / R が実機で回る)記載なし
実機起動iOS 17+ の RSD トンネル、pymobiledevice3 依存Pure Go の usbmux / lockdown / AFC / DDI
署名Apple ID(無料アカウント可)/ App Store Connect API キー.p12 + mobileprovision / アドホック / 署名なし
プラグインSwiftPM プラグインCocoaPods + SwiftPM + Native Assets + Cargokit
ホストWindows / Linux ネイティブ(WSL 不要)Linux。WSL 2 は「動くはずだが CI 対象外」

つまり内側のループ(書く・動かす・直す)は xcross、外側の成果物(署名済み IPA)は Ripley、という住み分けが現時点では成立する。片方だけで Mac が消えるわけではない、というのが正確な読み方だ。

なお、r/FlutterDev のスレッドを紹介する記事の一部に「Ripley が7日間の無料証明書で署名する」という要約が出回っているが、README にその記述はない。Ripley は署名材料(.p12 と mobileprovision)を利用者が持ち込む前提で、無いならアドホック署名にフォールバックする。無料 Apple ID による7日間有効のプロビジョニングは、どちらかといえば xcross 側の「Apple ID(無料アカウント可)」が触れている領域である。

それでも Apple から逃げられない2つの場所

ここが一番大事なところだが、「Mac 不要」は「Apple 不要」ではない。

第一に、Ripley は Apple のプロプライエタリファイルを一切同梱しない。利用者は Apple Developer Downloads から Xcode を取得し、その中から2つのディレクトリ——iPhoneOS.sdk と XcodeDefault.xctoolchain/usr/lib/swift/iphoneos——を自分で抜き出して ripley setup --sdk に渡す必要がある。Mac がある人向けに SSH でコピーするショートカットまで用意されている。

ripley setup --sdk user@mac:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk

そして Apple の Xcode and Apple SDKs Agreement は、Apple SDK の利用を Apple ブランドのハードウェア上に限定していると読むのが一般的な解釈で、この点は Apple Developer Forums でも繰り返し議論されてきた。Ripley も xcross も「Apple のファイルは配らない/再配布するな」と明記しているのは、この線の手前で止まるための設計判断だ。個人の実験で使うのと、会社の CI に組み込むのとでは、踏む必要のある確認がまったく違う。

第二に、リポジトリは v0.1.0、公開からまだ1日である。配布物は Linux x64 / arm64 のバイナリのみ、WSL 2 は CI テストマトリクスに入っていない。Swift toolchain は iPhoneOS SDK のバージョンと一致している必要があり、ここは xcross も「Swift を切り替えたら SDK を作り直せ」と警告している共通の地雷だ。

一方で、出口は思ったより塞がっていない。 IPA さえ手元にできれば、App Store Connect への提出自体は Mac なしで成立する。Apple の Transporter コマンドラインツールは macOS・Windows 11 に加えて Red Hat Enterprise Linux(64bit)をサポート対象に挙げているし、App Store Connect REST API によるビルドアップロードも OS を選ばない。「Linux で作った IPA を Linux から出す」という経路は、少なくとも技術的には繋がっている。

エンジニアへの影響 — 効くのは「Mac を捨てること」ではない

実務でこれが効くのは、開発機から Mac を追い出す場面ではない。効くのは CI だ。

Flutter 案件で iOS ビルドを回すとき、macOS ランナーの単価は Linux ランナーのおおむね数倍になる。PR ごとに iOS ビルドを回したいがコストで諦めている、というチームは珍しくない。Ripley が想定どおり動くなら、リリースビルドは従来どおり Mac、PR ごとの検証ビルドは Linuxという二段構えが成り立つ。ここだけでも請求額の桁が変わるケースがある。

もう1つは、Mac を持たない開発者を iOS 対応のチームに入れられるかという話だ。Windows / Linux のエンジニアが Flutter を書けても iOS の確認ができない、という制約は採用と業務分担の両方に効いてくる。xcross のホットリロードと Ripley の IPA 生成が両方安定すれば、この制約は「Apple Developer Program の枠を確保できるか」という別の問題に置き換わる。

ただし今日やるべきことは導入判断ではない。v0.1.0 のツールに本番パイプラインを預ける段階ではないので、手元の実プロジェクトで ripley build ios --ipa --no-sign が通るかだけを試す——Pod グラフが重い既存アプリほど、そこで落ちるかどうかの情報価値が高い。落ちたならその issue が、このツールが実用域に入る時期を決める。

まとめ

  • Ripley は Flutter の iOS ビルドを Linux に持ち込む Go 製 OSS。クロス版 gen_snapshot・Pure Go の Assets.car / Storyboard コンパイラ・Go 実装の USB デバイス層の3つで、Mac 必須の構造を外した。
  • 特に大きいのは release / profile の AOT ビルドが Linux で通る点。先行する xcross は debug(JIT)専用と明記しており、ここが未踏だった。
  • 一方で Apple SDK は利用者が Xcode から抜き出す前提で、ライセンス上の線引きは各自の判断に残る。「Mac 不要」は「Apple 不要」ではない。
  • IPA の提出経路(Transporter の Linux 版、App Store Connect REST API)は既に存在するため、理屈のうえでは Mac を一度も経由しない出荷パスが繋がる。

v0.1.0 のツールに本番を預ける段階ではない。それでも、Flutter が10年近く背負ってきた「iOS だけは Mac で」という前提が、技術的必然ではなく配布物の都合と Xcode 同梱バイナリの都合でできていたことを、このツールは具体的に示した。同じ方向の実装が複数同時に出てきている以上、この前提が崩れる側に賭けるのは、そう無茶な読みではない。

ソース