ねこ島

iOSのこととか、日常について雑に書きます

Kyash iOSアプリのビルド時間を短縮する取り組み

この記事は、 Kyash Advent Calendar 2022 の9日目の記事です。

はじめに

KyashでiOSエンジニアをやってます、@nekowenです。
Kyashモバイルチームの目標の一つに 生産性の向上 があります。
「今後会社の求める事業の成長スピードにモバイルチームが柔軟にスケールしていける状態を作りたい」という経緯があり、今の開発の障壁を減らしていくことを想定しています。
ビルド時間の短縮はその一環として取り組んでいるもので、今回はその内容について紹介いたします。

なぜ取り組むのか 🤔

モバイルの開発では基本、レイアウトの調整や動作の確認のため1日の間に数回〜数十回程度ビルドを行います。
最近ではSwiftUIの導入が進み、プレビューでの開発で事足りるケースもありますが、それでも初回及びSwiftUI View以外を変更した場合もビルドが必要になります。

このビルドにかかる時間はプロダクトの規模によって異なりますが、画面数やロジック、必要となるライブラリなど依存関係が増えれば増えるほど1回にかかるビルド時間が増えてしまいます。

Kyash iOSアプリも例外ではなく、フルビルドで5〜7分、差分ビルドで2〜3分程度時間がかかってしまっていました。 1時間に4〜5回差分ビルドするとしても最大で15分かかります。そうなると開発時間の1/4をビルド時間で埋めてしまう計算になり、そこそこの割合を占めていることがわかります。

待ち時間が長いほど機能開発の時間が伸びてしまいますから、これはチーム内の課題としてビルド時間を短縮していけるように改善していく方針となりました。

調査と対応方針 🗒

「ビルドに時間がかかっている」といっても、どの部分に手を入れれば良いでしょうか? XcodeにはBuild Phase毎にかかった時間をアウトプットしてくれる Build with Timing Summary があるのでこちらを使用しました。

Product -> Perform Action -> Build with Timing Summary を選択するとビルド時に測定してくれます。

実行すると以下のようにレポートが吐き出されます。

CompileC (613 tasks) | 628.295 seconds
CompileSwiftSources (36 tasks) | 323.218 seconds
CompileStoryboard (220 tasks) | 114.536 seconds
CompileXIB (273 tasks) | 95.150 seconds
...

これはフルビルドの場合で、差分ビルドの場合はこのようになりました。

SwiftEmitModule (1 task) | 34.190 seconds
SwiftDriver Compilation (1 task) | 33.775 seconds
SwiftDriver (1 task) | 4.711 seconds
...

なお、各項目の時間表記は「タスクの処理にかかった総時間」を表しています。実際のビルドではタスクが並行処理されるため表記の時間より早く完了します。

このように数値でみると、どの項目で時間がかかっているのかがはっきりとわかるため対策を打ちやすくなります。
今回の場合はCompileC, CompileSwiftSourcesに焦点を当てればよいことがわかりました。

CompileC

ログを見ると、Kyashで利用しているFirebase SDKが含むObj-Cのソースコードコンパイルが半数ほどを占めていました。 これは他のプロジェクトでもちらほら見受けられるのですが、Firebase SDKは多くの機能を持っているため、初回ビルドに時間がかかる傾向にあります。

そのためKyashではソースコードからのビルドではなく、Firebaseが提供するXCFrameworkを扱うことでビルドそのものを無くすことにしました。 XCFrameworkの管理にはCarthageを使ってバージョン管理を行う方法、FirebaseのGithub Releasesから直接ダウンロードする方法がありますが、まずはお試しとして直接ダウンロードして利用してみています。

これによりビルドタスクが半減し、ビルド時間としては30〜60秒ほど短縮できました 🎉

CompileC (613 -> 399 tasks) | 628.295 -> 162.848 seconds

CompileSwiftSources

アプリのコード・ライブラリ含めSwiftコードコンパイル系がこの項目にまとめられています。 ログを見る限り、Kyash iOSアプリのコードでちらほらコンパイルに時間がかかっているように見えました。

そこでメソッドと型推論にかかる時間を全て洗い出すことにしました。 OTHER_SWIFT_FLAGSdebug-time-expression-type-checkingdebug-time-function-bodies を指定することで、ビルドログの中にコンパイルにかかった時間を追記させることができます。

このデバッグ機能を利用して、コンパイル時間が長い順にテキストを吐き出させるようにしました。

xcodebuild -workspace HogeApp.xcworkspace \
-scheme HogeApp \
-destination 'platform=iOS Simulator,name=iPhone 11 Pro Max' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-expression-type-checking \
-Xfrontend -debug-time-function-bodies" | 
grep "[0-9]*\.[0-9]*ms" |
sort -nr |
uniq > result.txt

その結果、RxSwiftを利用している一部の複雑なコードで型推論30秒(!)ほどかかっているメソッドが見つかったので、明示的に型を指定することでこれを0.3秒以下に抑えることができました。

今後同じことが起きないように、型推論に300ms以上かかった場合にWarningを表示するようにしました。
設定方法としては、OTHER_SWIFT_FLAGS-Xfrontend -warn-long-expression-type-checking=300 を追加すると動作します。

Compile XIB, Storyboard

Interface Builderを用いて作られたStoryboardやXIBファイルのビルドにも一定時間がかかっていました。 ただKyashでは既存UIも含めてSwiftUIへの移行を進めており、この項目はいずれ改善されていく見込みです(その分Compile Swiftに寄るんじゃないかとは思ってますが。。。)

UIマルチモジュール化計画

こちらはまだ計画途中ではあるのですが、UI部分のマルチモジュール化を検討しています。 Kyash iOSアプリは元々以下のようなマルチモジュール構成で作られています。

[Kyash iOS]
L KyashUI
L KyashUseCase
L KyashRepository
L KyashEntity
L KyashUtility
...

この中でKyashUIは全てのレイアウトや機能を保持するため一番大きなモジュールとなっており、また機能開発による画面の追加などで肥大化が進んでいます。 モジュール分割のメリットの一つとして差分ビルドが働きやすくなる点があるのですが、これはモジュールが適切に分割されている場合において有効です。

今のKyashUIでは大きなUI単位としてモジュールが切られてしまっているので、このメリットが薄れてきてしまっているように思います。 そのため依存関係を見直し、KyashUIそのものをFeature単位などで細分化できないか試しています。こちらに関しては進捗があり次第別途記事を書こうと思います。

M1(Max) Macへの切り替え

ビルド時間の改善に向けて進めていたところ、M1 Macに切り替える機会がありました。 これはM1 MacのパフォーマンスがIntel Macより格段に高いことが知らされていたので、生産性向上にある程度貢献できそうという観点から会社側がモバイルチームに優先的に支給してくれました。

支給されたM1 Macのスペックは

  • チップ: Apple M1 Max
  • メモリ: 64GB
  • ストレージ: 1TB

となかなかハイスペックな構成で組まれていました。

気になるビルド時間への影響ですが、なんとフルビルドで2分きっかりに収まるようになりました。 計測データをとってみても数値が半減していることがわかります。

CompileC (676 tasks) | 628.295 -> 316.034 seconds
CompileSwiftSources (37 tasks) | 323.218 -> 111.771 seconds
CompileXIB (266 tasks) | 95.150 -> 27.195 seconds
CompileStoryboard (212 tasks) | 114.536 -> 25.813 seconds

あとIntel Macだとビルド中ファンが唸っていたのですが、M1 Macだとそれがほぼなくなったのが嬉しい点ですね。
少し悔しい感じもありますが、手っ取り早くビルド時間を短縮化するのであればM1 Max Macに切り替える、というのも一つの手かもしれません。

最後に

以上の取り組みにより、実際にビルド時間が短縮されたことによって快適に開発が進むようになりました。 ビルド時間は自分のみならず他のメンバーの開発効率にもダイレクトに影響する部分なので、調査・改善を継続して進めていくことが大切だと感じました。

Kyashではモバイルエンジニアを始め、プロダクトをより良いものにしていく仲間を募集中です。

興味がありましたらぜひ、ご連絡お待ちしております!

募集職種一覧 / 株式会社Kyash