Non-Fatal Error — アプリケーションを終了させず、プログラムの実行を継続できるエラーです。致命的エラーとは異なり、非致命的エラーはユーザーセッションを失うことなくキャッチ、処理、およびログ記録できます。Firebase Crashlyticsドキュメント(2024年)によると、本番アプリケーションで記録された全エラーの約70%が非致命的ですが、それらを無視すると技術的負債が蓄積され、ユーザーエクスペリエンスが徐々に低下します。非致命的エラーの適切な処理は、モバイル開発者の重要なスキルの一つです。
重要なポイント
Non-Fatal Error — プロセスを終了させない例外またはエラー状態です。アプリケーションは動作を継続しますが、データが読み込めない、リクエストが送信されない、インターフェース要素が表示されないなど、不正な状態にある可能性があります。ユーザーはエラーに気付かないか、メッセージを見てアプリケーションの使用を続けます。
非致命的エラーは常にプログラムに回復の道を残します。エラーハンドラは代替データを提供したり、操作を再試行したり、インターフェースのプレースホルダーを表示したりできます。主な目標はクラッシュを防ぎ、許容可能なユーザーエクスペリエンスを維持することです。開発者は各catchブロックで明示的に回復シナリオを計画する必要があります。
Instabug 2024によると、65%のユーザーが2回の失敗した操作後にアプリをアンインストールします。放置された非致命的エラーは蓄積され、全体的な品質を低下させます。非致命的エラーの体系的なログ記録と修正は、リテンションを向上させ、アプリストアの評価を上げる直接的な方法です。
ネットワークエラーはモバイルアプリケーションで最も一般的な非致命的エラーのタイプです。接続タイムアウト、ネットワーク喪失、不正なサーバーステータスコード — これらすべての状況はクラッシュなしでキャッチおよび処理されます。ユーザーには再試行オプション付きのサービス利用不可メッセージが表示されます。指数バックオフを使用した再試行パターンはネットワークエラーで一般的です。
不正なサーバー応答形式、必須フィールドの欠落、無効なデータ型 — パースエラーは、アプリケーションが不正なデータを適切に処理すれば非致命的です。典型的なアプローチは、デフォルトのフォールバック値を使用し、後でサーバーサイドで分析するためにリクエストコンテキストとともにパースエラーをログ記録することです。
画像読み込みの問題、不正なフォント、レイアウトエラー — これらはすべて非致命的ですが、ユーザーエクスペリエンスを低下させます。プレースホルダー画像とフォールバック値は、空の画面を回避し、エラーを目立たなくするのに役立ちます。React Nativeでは、UIエラーに対してフォールバックコンポーネントを表示するError Boundaryが使用されます。
計算エラー、状態の不一致、誤った画面遷移 — 論理エラーはクラッシュを引き起こさないことが多いですが、アプリケーションの不正な動作につながります。これらはクラッシュレポートを生成せず、ユーザーの苦情まで気付かれないため、体系的なログ記録と監視なしでは検出が困難です。
Non-Fatal Errorは、プログラムに動作を継続する機会を残す点で致命的エラーと異なります。致命的エラーはアプリケーションが回復できない状態です:nullポインタの参照外し、スタックオーバーフロー、メモリ不足。非致命的エラーはキャッチ、処理、実行継続が可能ですが、致命的エラーはアプリケーションの再起動が必要です。
| 特性 | Non-Fatal Error | Fatal Error |
|---|---|---|
| アプリ終了 | なし | あり |
| 回復可能性 | あり、catchブロック経由 | なし |
| ログ記録 | コードからrecordExceptionで | クラッシュレポーターのみ |
| UXへの影響 | 一時的な不便 | セッションの完全な失敗 |
| 例 | ネットワークタイムアウト、パースエラー | NullPointerException, OOM |
非致命的と致命的の境界は実装に依存する場合があります。あるアプリケーションではネットワークタイムアウトが非致命的として処理されますが(1〜2秒後に再試行)、別のアプリケーションでは致命的になる可能性があります(ハンドラがない場合のクラッシュ)。質の高いエラー処理は、潜在的に致命的な状況を非致命的に変え、アプリケーションの安定性を高めます。高い信頼性要件を持つモバイルアプリケーションを開発する際、エラー処理システムの設計は主要なアーキテクチャ上のタスクの一つです。組み込みの監視システムにより、チームは非致命的エラーが多数のユーザーに影響を与える前に迅速に検出して修正できます。
Firebase Crashlyticsは、モバイルアプリケーションで非致命的エラーをログ記録するための主要ツールです。recordExceptionメソッドを使用すると、アプリケーションを中断せずに完全なスタックトレースと実行コンテキストを含む非致命的例外をキャプチャできます。クラッシュレポートとは異なり、recordExceptionはキャッチした例外をログ記録するためにコードのどこでも呼び出せます。
fun fetchUserData(userId: String) {
try {
val response = apiService.getUser(userId)
updateUI(response)
} catch (e: IOException) {
Crashlytics.log("Network error for user $userId")
Crashlytics.recordException(e)
showRetryDialog()
} catch (e: JsonParseException) {
// Non-fatal: フォールバックデータを使用
Crashlytics.recordException(e)
showFallbackContent()
}
}
// カスタムキーを使用したログ記録
Crashlytics.setCustomKey("screen", "Profile")
Crashlytics.setCustomKey("api_version", "v3")
Sentryは、非致命的エラーに対してより詳細な診断を提供するCrashlyticsの代替手段です。Sentry SDKはcaptureExceptionメソッドを提供し、例外の詳細をサーバーに送信します。Sentryの主な利点は、類似の非致命的エラーを単一のイシューにグループ化し、再発頻度を分析し、breadcrumbs(エラー発生前のユーザーアクションのシーケンス)として実行コンテキストを提供することです。
すべての非致命的エラーをログ記録する必要はありません。期待される状態(接続がない場合のネットワーク障害)は選択的にログ記録できます。予期しないエラー(処理されたコード内のNullPointerException、無効なデータ形式、論理エラー)は常にログ記録する必要があります。各チームが重要度のしきい値を定義します:平均して、1日あたりユーザー1000人あたり10〜20のユニークな非致命的エラーが正常と見なされます。非致命的エラーの急増に対するアラートを設定することが重要です — これは新しいAPIバージョンの問題やリリース後の回帰を示している可能性があります。
基本的な処理メカニズムはtry-catchで、例外をキャッチして回復コードを実行します。ネットワーク操作では、指数バックオフによる再試行が一般的なパターンです。パースエラーでは、デフォルトのフォールバック値を使用し、後でサーバーサイドで分析するためにコンテキストをログ記録するアプローチが取られます。
func loadImage(from url: URL) -> UIImage? {
do {
let data = try Data(contentsOf: url)
return UIImage(data: data)
} catch {
Logger.shared.logError(error: "Image load failed: \(url)")
return UIImage(named: "placeholder")
}
}
func performRequest() async throws -> Data {
var lastError: Error? = nil
for attempt in 0..<3 {
do {
return try await URLSession.shared.data(from: url)
} catch {
lastError = error
try await Task.sleep(UInt64(pow(2, attempt)) * 1_000_000_000)
}
}
throw lastError ?? URLError(.unknown)
}
Result型 — 例外なしの代替アプローチです。関数はSuccessとFailureのバリアントを持つsealedクラスResultを返します。呼び出し元のコードは両方のバリアントを明示的に処理し、未処理のエラーを排除します。Result型は、型レベルでの非致命的状態の明示的な処理のために、Kotlin(標準ライブラリのResult
非致命的エラーの種類ごとに回復戦略を計画する必要があります:ネットワークエラー時のキャッシュデータの読み込み、パースエラー時のデフォルト値の使用、UIエラー時のコンポーネントの再初期化。良いプラクティスは、アプリケーションとの対話を完全にブロックせずに、エラーメッセージを含むトーストまたはスナックバーをユーザーに表示することです。回復可能なエラーと回復不可能なエラーを区別することが重要です — 後者については、画面の再起動やデータのクリアを提案するなど、回復戦略が異なります。以前の成功状態をキャッシュすることは、モバイルプラットフォームで非致命的エラーを処理する最も簡単で効果的な方法であることがよくあります。
よくある質問
警告(warning)は、コード内の潜在的な問題についてのコンパイラまたは静的アナライザーの警告です。非致命的エラーは、すでに発生したがクラッシュには至らなかったランタイム例外です。警告はコンパイル前に修正できますが、非致命的エラーはcatchブロックを介して実行時に処理する必要があります。
いいえ、過剰なログ記録は監視を散らかします。本番環境では予期しないエラーをログ記録し、期待される状態は無視する必要があります:オフライン時のネットワーク障害は選択的にログ記録できますが、処理されたコード内のNullPointerExceptionは常にログ記録する必要があります。各チームがアプリケーションのコンテキストに基づいて重要度のしきい値を定義します。
SwiftUIでは、@PublishedのerrorStateフィールドを持つObservableObjectを使用してエラー状態を追跡します。Viewは変更を購読し、代替コンテンツを表示します。iOS 17より前ではハンドラ付きのCombineが使用されていましたが、iOS 17以降は、リアクティブなUI更新のためにSwiftDataと@Observableマクロが使用されています。
はい、エラーが連鎖反応を引き起こす場合です。例:非致命的な画像読み込みの失敗が不正なUI状態を引き起こし、表示を試みた際にクラッシュにつながる可能性があります。各レベルでの質の高い非致命的エラー処理は、それらが致命的レベルにエスカレーションするのを防ぎます。
iOSでは非致命的エラーはthrow付きのdo-catchで処理され、Androidでは例外付きのtry-catchで処理されます。iOSはドメインとエラーコードを持つNSErrorを使用し、AndroidはJava/Kotlinの例外を使用します。CrashlyticsはrecordExceptionを介して両方のプラットフォームで同じように動作し、統一された監視インターフェースを提供します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。