クラウド Mac mini で App Store の多言語スクリーンショットを自動生成する

CI/CD ·約 9 分

クラウド Mac mini で App Store の多言語スクリーンショットを自動生成する

海外展開を進めるチームが App Store Connect に素材をアップロードする際、最初に必ずぶつかるのが同じ数字だ。14言語 × 6枚のスクリーンショット × 3種類のデバイスサイズ、合計でおよそ300枚近くにもなる。運営担当が手作業で撮り直すと丸1日を消費し、しかもステータスバーの時刻を直し忘れて撮り漏れることも多い。この種の繰り返し作業こそ、チームで唯一のMacBookを占有するのではなく、macOSのGUIを回せる専用マシンにまとめて任せるべきものだ。

なぜクラウドのMac miniで実行するのか

ローカルのノートPCでスクリーンショット作業を実行するには、構造的な弱点が2つある。1つ目は、シミュレータを4〜5台同時に立ち上げるとファンが全開になり、画面も占有されて他の作業ができなくなること。2つ目は、ローカル環境とリリースブランチがずれやすく、スクリーンショットに使ったコードと実際に審査に出すバージョンが食い違うことが頻発する点だ。この一連の作業を専用の物理Mac miniノードに移すことで、これらは一気に解消する。

スクリーンショット自動化の本質は「UIテストを一通り走らせ、その途中でスクリーンショットを撮る」ことなので、前提としてアプリがUIテストで目的の画面まで正常に到達できる必要がある。強制ログインや認証コードなどの仕組みには、事前にテスト環境用のスイッチを用意しておくこと。

fastlane snapshotでパイプラインを構築する

まずプロジェクト内でsnapshotを初期化し、SnapshotHelper.swiftSnapfileを生成する:

cd MyApp
fastlane snapshot init

Snapfileはこのパイプライン全体の設定の中核であり、言語リスト・デバイスリスト・出力先ディレクトリをここに固定しておくことで、実行ごとに手動で引数を渡す必要がなくなる:

devices([
  "iPhone 16",
  "iPhone 16 Pro Max",
  "iPad Pro (12.9-inch)"
])

languages([
  "zh-Hans", "zh-Hant", "en-US", "ja",
  "ko", "de-DE", "fr-FR", "es-ES",
  "pt-BR", "ru", "it", "id",
  "th", "vi"
])

scheme("MyAppUITests")
output_directory("./screenshots")
clear_previous_screenshots(true)
override_status_bar(true)
concurrent_simulators(true)

UIテスト側では対象画面でスクリーンショット関数を1行呼ぶだけでよく、多言語切り替えのロジックを意識する必要はない。fastlaneがlanguages配列を自動的に1つずつ回してくれる:

func testHomeScreenshot() {
    let app = XCUIApplication()
    app.launch()
    snapshot("01Home")
}

シミュレータの並列実行とリソースの境界

concurrent_simulators(true)を有効にすると、fastlaneはデバイス数に応じてシミュレータインスタンスを自動的に作成する。しかし「起動できる」ことと「同時にそれだけ起動すべき」ことは別問題で、本当のボトルネックはメモリだ。クラウドMac miniの代表的な2構成で見てみると:

機種 メモリ 推奨並列シミュレータ数 フル実行(14言語×3デバイス)の目安時間
Mini Basic(M4/16GB) 16GB 3〜4台 約55〜70分
Mini Standard(M4/24GB) 24GB 5〜6台 約35〜45分

推奨並列数を超えると、メモリ圧迫によりシステムがシミュレータプロセスをディスクへスワップし始め、スクリーンショットのジョブはむしろ待ち行列化して遅くなる。個々の言語でスクリーンショットが失敗し、再実行が必要になることも増える。安全な方法は、まずxcrun simctl list devicesで現在起動中のシミュレータ数を確認し、しきい値を超える場合はバッチに分けて実行することだ:

xcrun simctl list devices booted
fastlane snapshot --devices "iPhone 16" --languages "zh-Hans,ja,ko"
fastlane snapshot --devices "iPhone 16" --languages "en-US,de-DE,fr-FR"

命名規則とframeitによる枠付け

1回実行すると数百枚の画像ができるが、命名の乱れは後でApp Store Connectにアップロードする際に最もミスが起きやすいポイントだ。出力ディレクトリは言語コード/デバイス名/番号-画面名.pngの3階層構成にしておき、Snapfileのデフォルトの挙動に合わせること。自分で別の階層構造を上乗せしないのが望ましい。

デバイス枠付きの見せ方が必要な場合(App Storeの商品ページ自体ではなく、宣伝用ページで使うことが多い)はframeitを使う:

fastlane frameit

スクリーンショットのメタデータを読み取り、対応する機種の枠を自動的にマッチさせてscreenshots/framedディレクトリへ一括出力してくれる。Photoshopで1枚ずつ位置を合わせる必要はない。

よくある落とし穴と対処法

CI統合:定期的な差分出力

素材は毎回のリリースで全量を撮り直す必要はなく、UIに変更があった画面だけを更新すればよい。やり方としては、クラウドMac mini上に定期ジョブを組み、Gitのdiffで影響を受ける画面を特定し、対応するUIテストケースだけを再実行する:

git diff --name-only HEAD~1 HEAD | grep "Screens/" > changed_screens.txt

このロジックをself-hosted runnerでGitHub Actionsに組み込めば、PRがリリースブランチにマージされたタイミングで差分スクリーンショットが自動的にトリガーされ、人による最終確認を経てまとめてアップロードできる。この一連の処理を専用の物理ノード上で回すことで、チームの他のビルドジョブとリソースを競合することもなく、スクリーンショット撮影のために開発機を専有する必要もなくなる。

よくある質問

同時に起動するシミュレータは何台が適切ですか?

M4・16GBメモリの機種なら同時3~4台が目安です。それ以上だとメモリ圧迫でキューが詰まり、逆に遅くなるため2回に分けたほうが速く終わります。

スクリーンショットのステータスバーはどう統一しますか?

Snapfile に override_status_bar true を設定すると、fastlane が時刻を9:41、バッテリーを満タンに自動で揃えてくれるため、手作業での修正は不要です。

ビルド専用に独享Mac miniが必要ですか?

東京・ソウル・シリコンバレーの5拠点に物理ノードを配置。1日単位でレンタル可能、申込から10分でVNC/SSH接続情報をお届けします。

今すぐMac miniをレンタル