XcodeGenを活用し、多人数でのiOS開発と向き合う
こんにちは。
ビットキーにてスマートフォンアプリの開発を担当している斎藤です。
今回はiOS開発においてフィーチャーされることも多い「XcodeGen」について紹介させていただきます。
まえがき
まず初めに、XcodeGenとは 「ymlファイルをもとに、xcode.projファイルを生成する」ためのCommandLineToolです。
- https://github.com/yonaskolb/XcodeGen
弊社のiOSプロジェクトにおいては、そのほぼ全てにおいて本Toolが利用されています。
導入してみた、というような記事なども数多くヒットしますので、今やiOS開発のデファクトになりつつあるのではないでしょうか。
今回の記事では初心に立ち返って、このXcodeGenが果たしている役割を紐解いていこうと思います。
この記事の対象読者
・XcodeGen導入にあたってのメリット/デメリットを俯瞰したい方
・XcodeGen導入済みのプロジェクトに携わり始めた方
この記事で扱わないこと
・詳細な導入方法についてはこの記事では取り扱いません!
XcodeGenとは / モチベーション
まえがきにも書いたように、XcodeGenとは 「ymlファイルをもとに、xcode.proj / project.pbxprojファイルを生成する」ためのCommandLineToolです。
(ちなみにSwift製OSSなので、中身を読み解いてみるのも面白いかもしれませんね)
これらはiOSプロジェクトの構成ファイルそのものですので、言い換えるとiOSプロジェクトにおける「Target, Build Settings, Schema, Libraryとの依存関係 ...」といった、アプリの構成に関わるほぼあらゆる領域を一手に担うことを意味します。
iOSアプリの開発を始めていくと、通常はxcode.projをGit管理することになると思いますが、扱いが非常に難しいことにすぐに気づくと思います。
なぜならば、project.pbxprojファイルはXcodeが読み取るための巨大なテキストファイルであり、そのままGitに置いてしまうと開発中頻繁にConflictします(私自身、Libraryやファイルの追加タイミングでよくConflictするのを目にしました)。
/* Begin PBXBuildFile section */
00514FC444BBD5912D2DA768 /* ASession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE086BFB7E6ED9A5399A911 /* ASession.swift */; };
00CCCD1096655432EA8F0C82 /* ASession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23331C86B6B52A099CD13B46 /* ASession+Extension.swift */; };
0230819BB4D3172FD4446B68 /* BSessionEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A65051AD8CF3AD40BCD6B1 /* BSessionEmitter.swift */; };
...
また上述のこともあって、PullRequestに含められてもレビューできるようなものでもありません(頑張ればできるかもしれませんが・・・そこに労力を割くのは勿体ないですよね)
仮にConflict解消に失敗すると、XCodeが開けない事態に陥ったり、依存関係が壊れてBuildの失敗に繋がったりと、やはり大規模・多人数になればなるほど扱いづらいものとなります。
「こんなxcode.projをymlから生成するようにできれば、上記の問題群も解決できるよね?」というのが今回ご紹介するXcodeGenというツールになります。
メリット
・なんといっても Gitでxcode.projを管理しなくてよい
→開発者毎のLocal差分を意識しなくて良くなり、誰が触っても同じ環境を用意することが可能になる
・通常運用していると見逃しがちな "BuildSetting" や "Scheme" への変更がPR単位で差分として確認しやすい / 追いやすい
→特に、設定変更後になにか問題が起きた際にも切り戻しが楽になる(= 過去のymlを参照するだけで良い)というのもメリットと言えるでしょう。
・SigningやSwift Package ManagerなどもXcodeGenで管理することが可能
・Libraryの依存関係が明瞭になる
→SwiftPackageManagerの例ですと、下記のような形でymlに宣言可能になるので、XcodeGen未使用の場合よりも依存関係が明瞭になると言えるでしょう。
packages:
Alamofire:
url: git@github.com:Alamofire/Alamofire.git
from: 5.0.0
XCGLogger:
url: https://github.com/DaveWoodCom/XCGLogger.git
from: 7.0.0
デメリット
・(xcode.projが担っていたもの全てをymlで表現するので仕方がない気はするものの)ymlの記述量が多くなりがち
・現状のプロジェクトにおける BuildSetting などの設定値をXcodeGenで再現するにあたって、記法を調べるのが大変。また設定の総量が必要十分であるか確認もしづらい
・記法を間違えるとXCodeがCrashすることがある
→一例ですが・・SwiftPackageManagerのバージョン指定表記を間違えるとCrashします。大幅にymlに変更を加えた場合はどの記述に問題があるのか瞬時に把握するのは難しいでしょう。
デメリットへの対峙
まず【ymlの記述量が多くなりがち】について。
bitriseで利用するymlは、ファイル分割を行いincludeという形で参照可能です。よって、責務ごとに適切な粒度でファイル分割してあげることによって可読性を上げることが可能です。
include:
- xcodegen-deps.yml
- xcodegen-schemes.yml
次に【現プロジェクトのXCodeの設定値】について。
ありがたいことに、XCodeのBuildSettingなどを .xcconfigファイル に抽出してくれるツール(BuildSettingExtractor)が存在します。 こういったツールを活用することによって、Launchタイミングから一定程度成熟してきたプロダクトであっても、ある程度手軽にXCodeGenへの移行ができるようになるはずです。
実際に利用してみての感想
初めの導入時は デメリット の欄で記載したように、既存のxcode.projをymlで再現しなくてはならないのでハードルが高くなります。
またCI/CDにおいては当然、XcodeGenでxcode.projを出力するフローが必要になりますので、既に構築済みのフローを「XcodeGenを利用する形」に変更する必要もあるでしょう。
ですがXcodeGenの導入によってConflictに悩まされることがなくなるのはデメリットを大きく超えるメリットであると感じます。
特にリリースプロセスにおいて nightly, develop, production... といったように複数のGitBranchを活用した戦略を採用している場合、よりメリットを感じやすくなると思います。
最後にこれは副次的な効果かもしれませんが・・
XCodeに詳しくなれるというのもiOSエンジニアにとってはメリットと言えるでしょう。私自身、調べていく中でプロダクトにマッチしたTarget/Scheme/Configの活用術に出会ったり、最適なBuildSettingの模索に繋がったりもしました。
おわりに
以上がXcodeGenの解説となります。
この記事が「XcodeGenって何者? 利用することによってどういったメリットを享受できるの?」といった理解を深める一助になったら幸いです。
中・大規模プロジェクトでxcode.projの管理方法に悩みを抱えている開発者の方々は是非導入を検討してみてください!