📱

iOS アプリの dSYM を CI 経由で Crashlytics に 継続的に Upload する Tips

2022/04/11に公開

(2023年1月21日追記)
Xcode 14 からは Bitcode が廃止されたため、この対応が必要なくなりました。読んでくださった方ありがとうございました。

==

1日に1回、 dSYM をまとめてアップロードするスケジュールを作ることで CI 実行時間の浪費を避ける Tips を紹介します。

dSYM ( Debug SYMBOL )

iOS アプリ開発において、若干運用に苦労するポイントとして、 dSYM ( Debug SYMBOL ) 管理があります。
iOS アプリのクラッシュ情報を収集するために、多くのユーザーが Firebase Crashlytics を利用していると思いますが、 Firebase Crashlytics 上で収集したアプリの難読化されたクラッシュログを読みやすいフォーマットに復元するために dSYM ファイルをアップロードする必要があるからです。

https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?hl=ja&platform=ios

terminal
/path/to/pods/directory/FirebaseCrashlytics/upload-symbols -gsp /path/to/GoogleService-Info.plist -p ios /path/to/dSYMs

BitCode を ON にしているアプリだと......

通常は上記のページで書かれているような、 upload-symbols コマンドを実行すればよしなにアップロードできますが、 アプリの BitCode を ON にしているアプリだと、 AppStoreConnect にアップロードされるため、 AppStoreConnect にログインして、 dSYM ファイルをダウンロードしてくる必要があります。

ここで AppStoreConnect の2段階認証を乗り越えるために、SMS を経由してなんとかしようと紆余曲折した時期もあるんですが、現在では AppStoreConnect API ver.1.6 から該当URLが取得できるようになり、 Fastlane を利用して容易に取得することができるようになりました。

https://github.com/fastlane/fastlane/pull/19508
https://docs.fastlane.tools/actions/download_dsyms/

でもアプリバイナリを Upload してから dSYM が反映されるまでとにかく遅い!!

実際には CI 上ではこんな手順になると思います。

0. (テストとか)
1. $ xcodebuild archive
2. AppStoreConnect へ Upload

---
AppStoreConnect 上で Upload したアプリバイナリが反映されるのを待つ!!
---

3. $ upload-symbols を実行

この反映時間がアプリのサイズにも寄りますが、だいたい40分ほど待たされます。その間 CI は待機して待たなければいけません。 Fastlane の download_dsyms action にはこのために wait_for_dsym_processing wait_timeout といったオプションパラメータがついていて、待つことができます。

Fastfile
download_dsyms(
  app_identifier: "com.shimastripe.App",
  wait_for_dsym_processing: true,
  build_number: 100,
  wait_timeout: 3600
)

しかし、このやり方では CI は契約プランに応じて実行時間で従量課金が発生するため、とても燃費が悪いです。 Deploy するごとに何も作業しない40分強を消費することはなんとか避けたいです。
(あと AppStoreConnect の機嫌次第で遅延やエラーが起きて失敗するときもあります。)

ただ dSYM のアップロードが漏れるとうっかり急増するクラッシュが認知できない可能性もあるため、 AppStoreConnect に上げられたアプリバイナリの dSYM は基本漏れずに Crashlytics に送っておくことは実現したい...!! です。

やり方として2つの方法が考えられます。

  1. Deploy してから N 分後に新しいアクションをスケジュールする仕組みを構築する
  2. 1日に1回、上がった dSYM をまとめてアップロードするスケジュールを作る

1のやり方ができたら良かったんですが、これは CI の機能に依存してしまうかつ、遅延スケジュールができる CI って現状ある......? となったため、2の方法を紹介します。

1日に1回、上がった dSYM をまとめてアップロードする

Fastlane の download_dsyms action には after_uploaded_date というオプションパラメータがあります。これは 一定時刻から後に上がった dSYM ファイルをまとめてダウンロードできます。
つまり、実行時間から1日前を計算して、指定したものを定期実行してしまえば、1日に1回まとめて dSYM を上げておいてくれる仕組みができるわけです!!

こんなかんじ

Fastfile
binary_path = "${BUILD_DIR%Build/*}\/SourcePackages\/checkouts\/firebase-ios-sdk\/Crashlytics\/upload-symbols"

yesterday = DateTime.now - 1

download_dsyms(
  app_identifier: "com.shimastripe.App",
  after_uploaded_date: "#{yesterday.iso8601}"
)

upload_symbols_to_crashlytics(
  gsp_path: "#{PROJECT_DIR}/App/GoogleService-Info.plist",
  binary_path: "#{binary_path}"
)

# Delete downloaded dSYM files
clean_build_artifacts

解決!!

これで最大1日遅延しても、dSYM が適切にアップロードされる状態を1日5分ほどの実行時間で実現できます!! やった!!

これをスケジュールに登録して、CI に後は任せましょう!!
開発要件によりますが、平日だけ実行に留めてもいいかもしれませんね。

==
ちなみに Swift Package Manager 越しに firebase-ios-sdk を入れて upload-symbols コマンドを呼ぶ場合上記の書き方で DerivedData 越しにスクリプトを実行することができます。便利ですね。

https://firebase.google.com/docs/ios/swift-package-manager?hl=ja

Discussion