XcodeGen で Flutter の .xcodeproj 管理を手軽にする

2024/06/17に公開

はじめに 🎉

XcodeGen を用いて ios/Runner.xcodeproj を生成する Flutter のサンプルリポジトリを作成しました!
https://github.com/lllttt06/xcodegen_flutter_sample

dev, prd の 2 つの Flavor ごとに以下の出しわけを想定した project.yml を作成しています。

  • アプリアイコン
  • アプリの表示名
  • Bundle ID
  • Firebase Project

project.pbxproj のつらみ 😢

Flutter で iOS アプリを開発していると、時折 Xcode 開いて編集する機会があると思います。
Xcode の GUI で編集した後に ios/Runner.xcodeproj/project.pbxproj の diff を見てみると👇のように非常にわかりにくい差分が生まれていることがあります。

https://github.com/react-native-community/cli/issues/652 より

iOS 開発では複数人が Xcode を操作することでこのファイルのコンフリクトに悩むことが多々ある[1][2]ようで、この問題を解決するツールの一つに XcodeGen があります。
https://github.com/yonaskolb/XcodeGen

XcodeGen とは 💻

XcodeGen は以下のような project.yml などの yml ファイルから、Xcode の設定である .xcodeproj を自動生成してくれるツールです。Flutter では flutter create で生成される ios/Runner.xcodeproj というファイル群がこれに該当します。

project.yml
name: Runner

options:
  xcodeVersion: '15.4.0'
  developmentLanguage: ja
  deploymentTarget:
    iOS: 16.0

include:
  - xcodegen/setting/base.yml

configs:
  Debug: Debug
  Debug-dev: Debug-dev
  Debug-prd: Debug-prd
  Profile: Profile
  Profile-dev: Profile-dev
  Profile-prd: Profile-prd
  Release: Release
  Release-dev: Release-dev
  Release-prd: Release-prd
...

XcodeGen の詳しい使い方や説明はわかりやすい記事が多数ありますのでそちらを参照ください。

SPM(Swift Package Manager) との比較 👀

同様の問題の解決策として 2024 年 6 月現在、 iOS 界隈では XcodeGen よりも SPM を使う流れが加速しているようです[3][4][5][6]。Flutter でもこちらの PR で SPM の対応が merge されています。
https://github.com/flutter/flutter/pull/146256

しかし Flutter v3.22.0 の段階では使用できないこと、パッケージ側の対応がされていない場合は CocoaPods との併用が必要になるため今回は採用しませんでした。

Flutter 開発に XcodeGen を活用する 🤖

Flutter 開発では project.pbxproj がコンフリクトすることはあまりありませんが、前述したように差分が非常にわかりにくく、PR のレビューなどに苦戦する問題があります。

XcodeGen の解説記事や情報のほとんどは、iOS のネイティブ開発用であるため今回 Flutter で XcodeGen を活用するサンプルのリポジトリを作成しました 🚀
https://github.com/lllttt06/xcodegen_flutter_sample

このリポジトリでは dev, prd の 2 つの Flavor に対応した Flutter アプリの .xcodeproj を生成する project.yml を作成しています。

よくある Flutter のユースケースを想定し、Flavor ごとに以下の要素を分けるようになっています。

  • アプリアイコン
  • アプリの表示名
  • Bundle ID
  • Firebase Project

使い方は簡単で .tool-versions に定義されている各種ツールを用意(本リポジトリでは asdf を使用しています) した後、以下のコマンドで .xcodeproj を作成、アプリを実行するだけです。

cd ios && xcodegen generate
flutter run --flavor dev(or prd)

XcodeGen 用のファイル群 📁

XcodeGen に関わるファイル群の構成は以下のようになっています。

ios
├── project.yml
└── xcodegen
    ├── script
    │   ├── check_pods_manifest.sh
    │   ├── copy_pods_resources.sh
    │   ├── crashlytics_upload_symbols.sh
    │   ├── embed_pods_frameworks.sh
    │   ├── run_script.sh
    │   ├── select_google_service_info_plist.sh
    │   └── thin_binary.sh
    └── setting
        ├── base.yml
        ├── debug-dev.yml
        ├── debug-prd.yml
        ├── debug.yml
        ├── profile-dev.yml
        ├── profile-prd.yml
        ├── profile.yml
        ├── release-dev.yml
        ├── release-prd.yml
        └── release.yml

ios/xcodegen/script/ には、 Xcode 上の Build Phases タブで定義されているスクリプトをそれぞれ切り出しています。
https://github.com/lllttt06/xcodegen_flutter_sample/blob/c2e796729db4557d3544b0de2f68460d7505c372/ios/xcodegen/script/run_script.sh#L1-L3

Xcode 上の Build Phases タブ
ios/xcodegen/setting/ には、 configs ごとの Build Settings タブの内容を記述しています。base.yml で各 config ごとの値の include と共通設定の記述を担っています。
https://github.com/lllttt06/xcodegen_flutter_sample/blob/c2e796729db4557d3544b0de2f68460d7505c372/ios/xcodegen/setting/base.yml#L1-L62

メインとなる project.yml の全体像は以下の通りです。各項目の詳細な説明については公式のドキュメントを参照してください。
https://github.com/lllttt06/xcodegen_flutter_sample/blob/c2e796729db4557d3544b0de2f68460d7505c372/ios/project.yml#L1-L191

Firebase Project を Flavor ごとに分ける場合 🔥

Firebase のプロジェクトをセットアップした後、各 Flavor ごとに使用したいGoogleService-Info.plistios/GoogleService/{Flavor} 配下に配置します。
さらに build 時に ios/ に Flavor ごとの GoogleService-Info.plist をコピーするスクリプトを実行する必要があります[3:1]。このための設定はコメントアウトされているので、必要に応じてコメントアウトを解除して利用してください。
https://github.com/lllttt06/xcodegen_flutter_sample/blob/c2e796729db4557d3544b0de2f68460d7505c372/ios/project.yml#L63-L68

Firebase Crashlytics を使用する場合 💥

コンソール上のログを見やすくするために postBuildPhase で dSYM のアップロードをする必要があります。こちらもコメントアウトされているので、必要に応じてコメントアウトを解除して利用してください。

https://github.com/lllttt06/xcodegen_flutter_sample/blob/c2e796729db4557d3544b0de2f68460d7505c372/ios/project.yml#L76-L84

さいごに 🎬

XcodeGen は最初のキャッチアップが大変なので、その一助になれば幸いです!
@_cokaholic さんに色々教わりながら作成しました!大感謝です!

脚注
  1. https://qiita.com/satoru_pripara/items/2caef1daa2a35b36a0a0 ↩︎

  2. https://cam-inc.co.jp/p/techblog/405650318236320839 ↩︎

  3. https://qiita.com/takehilo/items/879ddd4e18672dd372fd ↩︎ ↩︎

  4. https://www.m3tech.blog/entry/2023/01/20/120000 ↩︎

  5. https://moneyforward-dev.jp/entry/2023/08/23/120000 ↩︎

  6. https://tech.timee.co.jp/entry/2021/08/24/160205 ↩︎

Discussion