TestFlight のビルド番号が食い違う問題と、審査に出す前の7つの自動検査
TestFlight のビルド番号(CFBundleVersion)を上げても git に残らず、App Store Connect の番号と食い違いました。番号の決め方とアーカイブを1つのコマンドにまとめ、App Store 版では接続先や切り替えの部品など7項目を自動で検査するようにした記録です。

TestFlight のビルド番号(CFBundleVersion。同じ番号は2回アップロードできない)の食い違いは、番号を決めてアーカイブ(アップロード用のまとまり)を作るまでを1つのコマンドにまとめて防ぎました。番号は XcodeGen の設定ファイル(project.yml)に書き、App Store 版ではアーカイブの中身を7項目で自動検査して、通った時だけ番号を記録します。
TestFlight(Apple のテスト配信)にアプリを上げるたびに、ビルド番号を1つ上げる必要があります。同じ番号は2回アップロードできないからです。私たちの業務アプリ(Kaitori Base)では、この番号の更新が git に残らず、App Store Connect に上がっている番号とリポジトリの番号が食い違いました。
そこで、番号を決めてアーカイブ(アップロード用のまとまり)を作るまでを1つのコマンドにまとめました。審査に出す App Store 版では、さらに、できたアーカイブの中身を7つの項目で自動で検査し、通った時だけ番号を記録するようにしています。
TestFlight のビルド番号が git に残っていなかった
最初は、番号を上げる小さなスクリプトと、Xcode でのアーカイブを、別々に行っていました。番号を上げてアーカイブし、アップロードしても、その番号の変更をコミットし忘れることがありました。

もう1つ、番号の置き場所の問題もありました。私たちは XcodeGen(設定ファイルから Xcode のプロジェクトを作る道具)を使っています。プロジェクトを作り直すと、Xcode の中で直接書き換えた値は消えてしまいます。そこで、ビルド番号は XcodeGen の設定ファイル(project.yml)に書くことにしました。

番号を決めてアーカイブするまでを1つのコマンドに
番号を上げるスクリプトをやめて、番号の決定とアーカイブを1つのコマンドにまとめました。

# 過去のアーカイブの Info.plist から、ビルド番号の最大を探す
max=0
for plist in ~/Library/Developer/Xcode/Archives/*/*.xcarchive/Info.plist; do
v=$(/usr/libexec/PlistBuddy -c 'Print :ApplicationProperties:CFBundleVersion' "$plist" 2>/dev/null || echo 0)
[ "$v" -gt "$max" ] 2>/dev/null && max=$v
done
next=$((max + 1))
# リポジトリの番号のほうが大きければ、そちらを使う
[ "$repo_ver" -ge "$next" ] && next=$repo_ver決めた番号は project.yml に書き込み、コミットまでを同じコマンドの中で行います。人がするのは、最後のアップロードだけです。
審査に出す版では、アーカイブの中身を7項目で検査する
TestFlight で自分が試す版と、審査に出す App Store 版は、同じアプリでも中身が違います。App Store 版は本番のサーバーにだけつながり、テスト環境への切り替えのボタンは入っていません。この違いを、アーカイブができたあとに、中身を開いて確かめます。

| 検査 | 確かめること |
|---|---|
| 1 | ビルドの構成が Release(App Store 用)であること |
| 2 | 接続先が本番であること |
| 3 | テスト環境への切り替えの部品が入っていないこと |
| 4 | 通知の拡張(通知を受け取る小さなプログラム)が正しく入り、本体と同じ App Group の権限があること |
| 5 | 本体と通知の拡張で、バージョンとビルド番号が一致し、狙いどおりであること |
| 6 | iPhone 専用であること(本体と拡張の両方) |
| 7 | プライバシーの申告ファイル(PrivacyInfo.xcprivacy)が入っていて、壊れていないこと |


検査3が、実は何も確かめていなかった
検査3は、テスト環境への切り替えの部品が、アプリの実行ファイルに入っていないことを確かめるものです。最初は、その部品だけが持つ日本語の文字を、実行ファイルの中から探していました。

検査を作ったら、「通る時に通る」だけでなく、「通ってはいけない時に止まる」ことも確かめる必要があります。いつも通る検査は、無いのと同じです。
まとめ

- ビルド番号の更新が git に残らず、アップロード済みの番号と食い違った
- 番号は「過去のアーカイブの最大+1(リポジトリの値が大きければそちら)」で決め、アーカイブと同じコマンドで記録する
- App Store 版は、アーカイブの中身を7項目で検査し、通った時だけ番号をコミットする。失敗したら何も残さない
- 検査そのものが効いているかを、止まるべき場面で確かめる
App Store 版に切り替えのボタンを入れない理由は、次の記事に書いています。
アプリ開発本番のアプリから「テスト環境に切り替えるボタン」を消した理由と、#if DEBUG では足りなかった話本番とテストの接続先を切り替えるボタンを、開発用と TestFlight 用のビルドにだけ出し、App Store 版には入れないことにしました。TestFlight のビルドは Debug ではないので #if DEBUG では分けられず、SWIFT_ACTIVE_COMPILATION_CONDITIONS の独自フラグで切った方法です。
この記事は、AI(Claude)の支援を受けて作成し、筆者が内容を確認しています。
