この記事は、ゆめみ Advent Calendar 2021 の3日目の記事です。
ちょっと間に合わなかった
はじめに
Firebase Crashlyticsはよく使われるクラッシュ検知ツールです
別のクラッシュ検知ツールからCrashlyticsへ載せ替えが必要になったのですが
一定期間は元のツールとCrashlyticsを併用して欲しい要件へ対応した際のメモです
環境
- OS:macOS Big Sur 11.6.1
- Swift:5.5
- Xcode:13.0 (13A233)
現象
クラッシュレポートがCrashlyticsか他ツールのどちらかにしかレポートされない
原因
クラッシュ検知ツールは概ねtop-level error handlerを利用して、クラッシュ検知などを行なっていると考えられます
ただtop-levelとあるため、一つしか登録できず複数のクラッシュ検知を有効にした場合に、競合してしまう場合があります
対応
1. 致命的な例外(NSException)の場合
func exceptionHandler(exception: NSException) {
UserDefaults.standard.setValue(ExceptionModel(exception), forKey: "record")
}
NSSetUncaughtExceptionHandler(exceptionHandler)
2. Swiftランタイムエラーの場合
let signalHandler : @convention(c) (Int32) -> Void = { (signal) in
UserDefaults.standard.setValue(ExceptionModel(signal), forKey: "record")
exit(signal)
}
for code in [SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP] {
signal(code, signalHandler)
}
3. 起動時
if let record = UserDefaults.standard.object(forKey: "record") as? ExceptionModel {
UserDefaults.standard.removeObject(forKey: "record")
// TODO: Crashlytics & crash report tool recording
print(record)
}
解説
NSSetUncaughtExceptionHandlerを使うとキャッチされなかった致命的な例外をハンドリングすることができます
しかし、これだけだとSwiftのランタイムエラーがハンドリングできないので、シグナルハンドラを利用して補足します
シグナルハンドラ内では、データの保存のみ行います
起動時に、エラーレコードがあればCrashlyticsやクラッシュ検知ツールへレコードを送信し、レコードは削除します
問題点
Crashlyticsやクラッシュ検知ツールが提供しているリッチなログ(スタックトレース等)は自力で取得しないと、レポートされません
いい感じにログ収集してくれるExceptionModel
の作成が必要です
備考
実際にはPLCrashReporterなどを使うか、この様な要件は回避しましょう