Event

iOSアプリのビルド番号で色々な情報を確認できるようにする!

アプリのビルド番号で色々な情報を確認できるようにする!

この記事は2023/03/22の集まれSwift好き!Swift愛好会 vol.73 @G’s ACADEMY TOKYOの談義で使用した記事です。

アプリを開発していて、そのアプリを社内で配信する時、例えばTestFlightで配信して動作確認をするときに、困るのが、どのビルドがいつ、もしくはどのブランチでビルドされたのか?などがわかりにくいということです。

検証用に頻繁にアプリのビルドをアップロードしている場合、TestFlightのアプリ一覧↑からは、どのアプリがどの差分のものかわかりにくいし、TestFlightのアプリのビルドの詳細画面では テスト対象という欄にReleaseNoteのようなものを書けますが、アップロードごとに毎回設定するのも面倒です。

この中で変更できる要素といえば、

  1. アプリのアイコン
  2. アプリ名
  3. アプリバージョン
  4. アプリのビルド番号

ですが、①~③を設定変更するのは現実的ではないので、今回は④の ビルド番号を色々操作することで、それぞれのビルドがどのようなものかを少しでもわかりやすくしたいと思います。
登場するコードは主にShellのコードです(Swiftは登場しません笑)

ビルド番号を色々操作してみる!

そもそもビルド番号とは?

ビルド番号とは、アプリのバージョンとともに、アプリのビルド情報を特定する要素の1つです。

一般的にアプリのビルド一覧には、v1.0.0(1)のように書いてあることが多いですが、このカッコの中の数字がビルド番号、その前がアプリのリリースバージョンです。
同じバージョンのビルドの中で、それぞれがどのビルドかを特定する時に使用されるものです。

これらの情報はBundleResourceの中で管理されていて、以下のKeyで指定することができます

  1. CFBundleShortVersionString: アプリのリリースバージョン
  2. CFBundleVersion: アプリのビルド番号

今回の目標は、指定のビルド番号を上記の CFBundleVersionの値として設定することになります。

CFBundleVersionの情報は、公式ドキュメントに記載があります。
情報を抜粋すると、以下の通りです

  1. ピリオドで区切られた 1 ~ 3 個の整数で構成される文字列
  2. Semantic Versioningと同じように、[Major].[Minor].[Patch]の形式で指定
    • 各セクションは省略してもよいが、その場合、0を代わりに挿入して処理される
      • 例:  10 → 10.0.0
    • ビルド番号を比較するときは、SemanticVersioningと同じルールで計算
    • 4セクション以上を設定することもできるが、4つ以上のセクションは処理では無視される
  3. リリースバージョンとビルド番号を組み合わせた文字列がアプリ内で一意にならないといけない

またここには書かれていないですが、以下のTestFlightにipaをアップロードする時には以下のチェックも実行されます。

  1. 前回のビルド番号よりも大きい
    • SemanticVersioningとして比較した時に、同じバージョンの前回アップロードしたビルドよりもそのビルド番号が大きくないと、以下のようにエラーになってしまう。
    • “The bundle version must be higher than the previously uploaded version”
  2. 18桁以内でないといけない
    • これは試行錯誤している時にTestFlightにあげて、エラー↓に書いてあったのを見てから知りました。
      • “Asset validation failed – This bundle Payload/{IPA名}.app is invalid. The value for key CFBundleVersion [{ビルド番号}] in the Info.plist file must be no longer than 18 characters.”
    • あまり長いビルド番号はダメってことですね

なので、ビルド番号を設定するときは、上記のルールを満たす仕様にする必要があります。

参考: ビルド番号を自動操作するAPI

元々標準でアプリのビルド番号を変更する agvtoolというものがありますが、これはビルド番号の自動インクリメントなどに対応したもので、柔軟にビルド番号を変更できるものではないので、今回は見送りました
 fastlane の increment_build_number も内部的にこれを使っているようです。

参考: agvtool の使い方

ビルド番号に設定する条件の整理

ではCFBundleVersionの仕様や制約がわかったところで、今回のビルド番号をどうするか考えましょう

現在のアプリでは、ブランチ名にはチケットのID(親子)を含めるようになっていました。
例: feature/tichet-1234/tichekt-5678

なのでブランチ名から、ブランチの種類(master or develop or featureなど)と子チケットIDを取得して、それはわかるようにしたいです

また①ビルドごとにビルド番号を一意にしないといけない、②前回のビルド番号よりも大きくないといけない、のルールを考慮して、ビルド時間の日時を取得して、それをビルド番号のMajorVersionに設定することにしました。

また開発や検証の都合で、同じBundleIDのアプリだが、特殊テスト用のビルドなどがわかりやすくなるようにしたかったので、それがわかる桁も用意する必要があります。

したがってまとめると以下のようになります

{①ビルド時間の日時}.{②ブランチの種類}.{③チケットID}.{④オプション用の数字}

①は全体の桁数を考慮して、最初の20は省略し、2023/03/22 19::00 の場合は2003221922となるようにします

②はあらかじめルールを以下のように決めます
1→master
2→develop
3→feature
4→release
5→hotfix  などなど

③のチケットIDは最大4桁を想定しています
feature/hotfix以外など、チケットIDがブランチ名にない場合はこのセクションは省略されます。

④はそのビルドの種類によって、1を付与するようにします。
なおこのオプションは、master, develop, releaseブランチでのみ付与される想定です

これで以下のような桁数になり、18桁以内に収まります。
{①: 10桁}.{②: 1桁}.{③: 4桁}.{④1桁}

なおmasterの本番の配布ビルドの場合は、②~④は省略します。

仕様のビルド番号を生成する

先に実装したサンプルコードを記載します

日時

仕様ではYYYYMMDDhhmm形式の日時で最初の2桁を省略したものを使用します
今回は以下のような処理を実装しました

# Get `YYYYMMDDhhmm` formatted dateTime 
DATETIME=`TZ=UTC-9 date '+%Y%m%d%H%M'`
# Drop first 2 characters
SHORTENED_DATETIME=${DATETIME:2:10}

なお日時については、Timezoneによって変わる場合があるので、実行環境のTimezoneに依存しないように、Timezoneを統一するなどの対応が必要な場合があるかもしれません

ブランチ名

ブランチ名の文字列は、LocalのMacの場合、Gitコマンドで取得します(①)
MacとCIで取得方法を分けているのは、CIの場合、checkout先がHEADになっている場合があり、正しいブランチ名が取れない場合があるためです
大抵のCIの場合は、CIが定義している環境変数からブランチ名を取得することができると思うので、そこから取得します

あとはブランチ名にmasterなどを含んでいるかどうかを判定し、それに合わせた数字を設定していきますf

なお本番のリリース用のアプリでは省略するので、その判定は、Xcodeの環境変数のCONFIGURATIONを利用しています(今回の場合、Production-Releaseのconfigurationがそれに該当します)

if [ -n "$CI" ] && "$CI" ; then
    echo "Run in CI"
    # ①
    BRANCH_NAME=$SOURCE_BRANCH # Add the appropriate environment variables used in CI.
else
    echo "Run Not in CI"
    BRANCH_NAME=`git rev-parse --abbrev-ref HEAD`
fi

if [[ "$CONFIGURATION" = "Production-Release" ]]; then
    # Omitted in release apps
    BRANCH_NAME_NUMBER=""
elif [[ "$BRANCH_NAME" == *master* ]]; then
    BRANCH_NAME_NUMBER=".1"
elif [[ "$BRANCH_NAME" == *develop* ]]; then
    BRANCH_NAME_NUMBER=".2"
elif [[ "$BRANCH_NAME" == *feature* ]]; then
    BRANCH_NAME_NUMBER=".3"
elif [[ "$BRANCH_NAME" == *release* ]]; then
    BRANCH_NAME_NUMBER=".4"
elif [[ "$BRANCH_NAME" == *hotfix* ]]; then
    BRANCH_NAME_NUMBER=".5"
else
    BRANCH_NAME_NUMBER="${BRANCH_NAME} is Non-Defined-Branch-Name"
fi

チケット番号

チケット番号は今回の運用ではブランチ名から取得します。
運用では、例えばfeature/ticket-{親チケットID}/ticket-{子チケットID}のようになっているので、該当の子チケットのIDを引っ張り出す必要があります(この運用が正しいかは置いておいて)

例: ブランチ名がfeature/ticket-1234/ticket-56785678を取得したい

正規表現を使うんだろうなと思ったのですが、正規表現があまり得意ではないのでどうすればいいかわからず、エンジニアと人生コミュニティで相談したところ、bannzaiさんが優しいアドバイスをくれました!(とりあえず⭐️をいっぱいつけた!)

上記を参考に最終的に以下のような正規表現を作りました!

TICKET_ID=`echo $BRANCH_NAME | sed -E 's/.*-([0-9]{3,}).*/\1/'`

オプション

オプションは簡単で、Configuration変数を元に、特定のconfigurationの時だけ、数字を付与するようにします

case "${CONFIGURATION}" in
"Production-Release-Log") # To add log code even if the environment is Production
    BUILD_CONFIGURATION_TYPE="0";;
*)
    BUILD_CONFIGURATION_TYPE="";;
esac

生成したビルド番号をセットする

最後にこれまでの処理を元に、CURRENT_PROJECT_VERSIONにその値を設定します

今回は以下のようにxcconfigファイルに変数が定義したので、その値を書き換える処理を記載します

// Build number (automatically changed) (= no spaces before or after)
CURRENT_PROJECT_VERSION=xxx.xxx.xxx

sedコマンドを使って、以下のようにCURRENT_PROJECT_VERSION=の表記がある行を置き換えます(もう少しいい書き方がある気がするので、もしご存じの方がいれば教えていただけるとありがたいです!)

sed -i "" -e "s/CURRENT_PROJECT_VERSION=.*/CURRENT_PROJECT_VERSION=${BUILD_NUM}/g" $CONFIG_FILE_PATH

この処理を実行してから、これでアプリをビルドしてみると、想定通りにビルド番号が表示されるようになりました!
↓の場合は、

  1. 2023/03/14 15:58にビルドされた
  2. 4703のチケットの修正をした
  3. featureブランチ

のアプリということがわかります!

まとめ

今回はビルド番号を色々操作して、そのビルドのことがわかるようにしてみました。
これでビルドを指定する時に、「○時にビルドしたやつ〜」とか、「○ブランチの最新版で!」などの指定ができるようになりました!

本来はReleaseNoteを操作すべきだとは思うのですが、毎回ビルドするたびに手動で書くのもめんどうなので、とりあえずビルド番号を色々操作してみました
他の手段としては、もししっかりコミット名を書いているなら、自動でコミットの情報をReleaseNoteに転記するのも情報を特定する上では有効かもですが、ReleaseNoteの情報はアプリの一覧では確認できず、詳細画面に行かないとわからないので、この辺りは悩ましいところです

とりあえず今回は色々工夫して、ビルド番号からアプリ情報を少しでも多く読み取れるようにするという、ちょっとした実験の様子をお伝えしました!

+1

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA