はじめに
iOSアプリ開発において、リソースにタイプセーフにアクセスするためのソースコードを自動生成する SwiftGen や R.swift のようなツールをよく利用すると思いますが、みなさんはその自動生成されたソースコードは Git 管理下においていますか?
私の開発中のアプリではこれまで Git 管理下においていたのですが、アプリの要件により次のような状況になりました。
- Build Configuration に応じて自動生成されるコードに違いが出るようになった
- ソースコード以外にも Build Configuration に応じてアプリにバンドルさせるファイルを切り替える必要性がでてきた
このような状況ですと、Build Configuration によってファイルが変化してしまうため、ビルド時に自動生成するファイルは Git 管理下におかないようにしました。が、その中でつまづきポイントがあったのでメモとして残します。
Git 管理外にすると...
Git コマンドで自動生成ファイルを Git 管理対象外にします。
$ git rm path/to/Generated/file
Xcode プロジェクトツリー上ではそのファイルが赤色で表示され、存在しないことがわかります。
そして、自動生成されてもコミット対象にならないよう、.gitignore ファイルに自動生成先ディレクトリを追加しました。
path/to/Generated
この状態でビルドを実行してみたところ、次のようなビルドエラーとなってしまいました。
error: Build input files cannot be found: 'path/to/Generated/Asset.swift', 'path/to/Generated/StoryboardScenes.swift', 'path/to/Generated/Strings.swift', 'path/to/Generated/StoryboardSegues.swift' (in target 'MyApp' from project 'MyApp')
自動生成のタイミングを確認するも...
自動生成のタイミングがソースコードのコンパイル時に間に合っていないのかと思い、Xcode プロジェクトの Build Phases を確認してみましたが、SwiftGen を実行する Run SwiftGen は Compile Sources よりも前のタイミングになっていないので問題なさそうです。
プロジェクトツリー上のファイルの存在チェックはこの Build Phases よりも前のタイミングで実施されるのでしょうか?
解決策
いろいろと調べて R.swift リポジトリのこの issue にたどり着きました。
First build always fails using the new build system with Xcode 10 Beta 6
どうやら Xcode10 の新しいビルドシステムでは、Build Phases で生成されたファイル等は成果物として明示してあげる必要がありそうです。ということで先程の Run SwiftGen の Output Files に自動生成されるファイルのパスを追加しました。
これにより、自動生成される前の状態でのXcodeプロジェクトツリー上での表示もこのように変わりました!ファイルアイコンは半透明のままですが、ファイル名は赤色ではなくなっています。
もちろん、ビルドも問題なく通るようになりました
2020/2/27追記 - この対応による問題と回避策
上記のような対応を実施した場合、自動生成の元となるファイルが変更されても再生成が行われないことがわかりました。これを回避するためには、自動生成の元となるファイルを Input Files に追加しなければならないようです。これは、SwiftGen や R.swift の Issue でも議論されています。
ただ、Input Files が複数に渡る場合(例えば、Storyboard を 1画面1ファイルにしているようなケース)ですと、ファイルを追加するたびにここのメンテナンスが必要となって厄介です。せめてフォルダを指定できればよいのですが、それでは意図した動作とならないようです。
SwiftGen については上記の Issue は "Apple Bug" とラベリングされて止まっており、 [New Feature] Generate xcfilelist for Script Build Phases で Input Files の代わりになる xcfilelist を生成するアプローチが提案されていますが、止まっていますね。
R.swift については v5 で対応されているようです。その修正は mac-cain13/R.swift/pull/468 で、うまい対応策が実施されています。
最終的に私の方では、Build Phase の前に走る Build scheme pre-actions で自動生成を行うことで回避しました。ただ、 SwiftGen/Issue/560のコメント にあるとおり、pre-actions ではスクリプトのエラーが Xcode の方で警告されないため、エラーに気づけないというデメリットがあります...
2020/10/8追記 - ↑の問題への対策
上記の「自動生成の元となるファイルが変更されても再生成が行われない」問題に対して、2つ対策方法ができたので紹介します。
- SwiftGen v6.4.0 の新機能を使用する
- Xcode 12 の新機能を使用する
SwiftGen v6.4.0 の新機能を使用する
2020/10/8 にリリースされた v6.4.0 で、.swiftgen.xml の記述を元に SwiftGen の入出力ファイル一覧 (xcfilelist) を出力するコマンド config generate-xcfilelists
が追加されました。
.swiftgen.xml を変更した際にこのコマンドを実行し、Build Phases の Run Script の入出力ファイルとして設定するだけです。
swiftgen config generate-xcfilelists --inputs SwiftGenInput.xcfilelist --outputs SwiftGenOutput.xcfilelist
Xcode 12 の新機能を使用する
Xcode 12 に Run Script を必ず実行するオプションができました。下図の Based on dependency analysis がそれです。このオプションはデフォルトでオンになってるので、これをオフにすることで入力ファイルの変更有無に関係なくスクリプトが実行されます。