STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

STORES レジ のビルド時間を改善したかった話

こんにちは! STORES レジ の開発をしている iOS / Android エンジニアの @satoryo056 です。
今回は STORES レジ のビルド時間を短縮するために実験したことを紹介します。

STORES レジ について

STORES レジ (以下、レジアプリ)は iPadOS 専用のモバイルアプリで、実店舗とネットショップの商品・在庫・売上の管理をしたり実店舗(オフライン)でのお会計をすることができます。
最近は 予約システムとの連携機能を実装 したり、長年課題だった技術的負債の改善 に取り組んだり、プロダクト監視の時間 を設けたりと開発面・運用面でアップデートを続けています。

product.st.inc

product.st.inc

product.st.inc

そんなレジアプリで、最近個人的に思うことがあります。

それは ビルドに時間がかかる ことです。

具体的には以下のことに悩んでいます。

  • 1行でも変更するとビルドに1分以上かかることが多々ある
  • Xcode Cloud でのテスト実行や TestFlight の配信に20分以上かかる
  • Preview のビルドが遅い(そもそもエラーでビルドに失敗することもある)

そこで私は、Xcode のビルドに時間がかかる理由を調べることにしました。

Xcode Build Timeline について

Xcode の機能に「Build Timeline」というものがあります。
WWDC 2022 でも紹介されていた機能で、パッケージのビルドや Swift ファイルのコンパイルなどにかかった時間をタイムラインで可視化してくれます。

Demystify parallelization in Xcode builds - WWDC22 - Videos - Apple Developer

実際に使用してみる

Xcode Build Timeline を使ってレジアプリのビルドのタイムラインを可視化すると以下のようになりました。

  • ビルドキャッシュあり:約40秒
  • ビルドキャッシュなし:約2分40秒

ビルドキャッシュあり

ビルドキャッシュなし

結果を見るとそんなに時間はかかっていないように感じます。
ただ、私は先に書いた "ビルドに1分以上かかることが多々ある" のがどうしても気になりました。
例えば View のテキストを変えたり、コメントを追加するだけでも時間がかかってしまいます(1行だけ変更した場合のビルド時間や Build Timeline は↑とほぼ同じでした)

そこでここからは View(UI)のコンパイルの時間に着目してみます。

  • ビルドキャッシュあり:約30秒
  • ビルドキャッシュなし:約40秒

UIのコンパイル部分(キャッシュあり)

UIのコンパイル部分(キャッシュなし)

キャッシュあり・なしで約10秒の差がありますが、View のソースコードを少し変更しただけで毎回ビルドに30〜50秒かかる理由が気になったため Xcode Build Timeline のログを調べました。
ログを調べると View が格納されているモジュールにある全ての Swift ファイルをコンパイルしていることが分かりました。

レジアプリのモジュール構成について

レジアプリでは、Swift Package Manager を採用しており、Xcode 上でレイヤごとにパッケージを分けています。

レジアプリのモジュール構成(画像はイメージです)

このうち UI モジュールで View が格納しており、画面ごとにディレクトリを分けています。
数えてみたところディレクトリは約50個ありました。

UI モジュールにあるディレクトリ(画像はイメージです)

さらに Xcode Build Timeline のログを確認したところ、UI モジュールにある Swift ファイルは全部で 770個 あることが分かりました。

つまり、ビルドのたびに毎回770個の Swift ファイルをコンパイルしていた のです!

さすがに1回のビルドで毎回770個の Swift ファイルをコンパイルしなくていいのではないかと思い、 Android のマルチモジュールの仕組みを導入できないか考えました。

※ そもそも差分ビルドが走らないのはおかしいのではないかとも考えましたが、今回は踏み込まないことにします。

Android におけるマルチモジュールのベストプラクティス

Android では以下の画像のように画面や機能(Feature)ごとにモジュールを分ける(マルチモジュール化する)ことが一般的とされています。

Navigation best practices for multi-module projects より引用

マルチモジュール化は各モジュールのビルドを並列で行うことができたり、モジュールごとに依存関係を整理できたりなどプロジェクトにとってメリットになります。
iOS でもマルチモジュールは採用されていますが、私が調べた限り iOS ではあまり Feature 単位でマルチモジュールにするアプローチは採用されていないように見えました。
※ 私の調べが足りないだけで実は結構採用されているのかもしれないので、間違っていたらぜひ教えてください!

早速レジアプリに導入してみたらどうなるか実験してみました。

レジアプリの UI モジュールをマルチモジュールに移行する

モジュール構成

既存の UI モジュール構成は以下のようになっており、全ての View が相互に依存する状態になっています。

既存の UI モジュール構成

UI モジュールをマルチモジュール化したあとのモジュール構成は以下になります。

  • Router: Feature モジュール間のナビゲーション(画面遷移)のプロトコルを定義します
  • UIRoot: 各 Feature モジュールの親にあたり、Router のナビゲーションを継承して画面遷移先の View を指定します
  • Feature: 既存の UI モジュールにあった各画面(ディレクトリ)をそれぞれモジュール(パッケージ)化しました
  • UI: 既存の UI モジュールをそのまま残しており、各 Feature が共通で利用する View などを格納しています

マルチモジュール化したあとのモジュール構成

Xcode Build Timeline でビルド時間を計測する

マルチモジュールに移行したあと、ビルド時間を計測したところ以下のような結果になりました。

  • ビルドキャッシュあり
    • 全体のビルド時間:約17秒(変更前は 約40秒)
    • 1つの Feature のビルド時間:約1秒(変更前は UI パッケージ全体で 約30秒)

マルチモジュール化後のタイムライン(キャッシュあり)

  • ビルドキャッシュなし
    • 全体のビルド時間:約2分37秒(変更前 約2分40秒 とほぼ変わらず)
    • 1つの Feature のビルド時間:約1秒
    • すべての Feature のビルド時間:約10秒(変更前は UI パッケージ全体で 約30秒)

マルチモジュール化後のタイムライン(キャッシュなし)

ビルドキャッシュなしの全体のビルド時間はほとんど変わりませんでしたが、Feature ごとのビルド時間(コンパイル)は約1秒となんと 97.5% も改善 しました!

マルチモジュール化のデメリットや困難

Feature ごとにマルチモジュール化することで、画面単位でのビルド時間は大幅に改善しました。
しかし、マルチモジュール化に移行するにあたりデメリットや困難なこともありました。

まず、UI モジュールにあるディレクトリを全てパッケージ化することが非常に大変ということです。
先に書きましたが、UI モジュールには約50個のディレクトリがあるため、50個のパッケージを追加する必要がありパッケージごとに Package.swift の依存関係を記述する手間がかかりました。

さらにパッケージを分けた後の UI モジュールでは各 Feature が共通で利用する View を持っていますが、各 Feature が参照するためにすべての View を public にする変更が必要になりました。

そして Router の仕組みを取り入れたことで、Router 側で各 View の引数を考慮する必要が出てきました。例えば、引数の中に View で定義している型がある場合、Router に依存しないような形で変更する必要がありました。

マルチモジュール化によってビルド時間が改善したことは嬉しかったですが、気軽に着手してしまったので、正直途中で心が折れかけました。

終わりに

STORES レジ のビルド時間の改善のために UI モジュールをマルチモジュール化した取り組みを紹介しました。
この取り組みを通して、マルチモジュール化による効果が見られたこととXcodeのビルドの仕組みを知ることができたことも収穫でした。

STORES では一緒に働く仲間を募集しています。このブログを見て STORES レジ に興味が出た!モバイル開発の話を聞いてみたい!という方がいましたらぜひお話ししましょう!

またオフィスでご飯を食べながら社員と話ができる BeerBash というイベントも定期的に開催しているので、そちらもぜひご参加ください。よろしくお願いします!

jobs.st.inc

coubic.com