未処理のcompletionHandlerによるクラッシュを防止するガード追加
未処理の completionHandler が呼び出されないと、WebKit が NSInternalInconsistencyException を送出しアプリ全体がクラッシュします。本 PR は、WKNavigationDelegate と WKUIDelegate 系メソッドにガードを追加し、デリゲートが解放されても安全にハンドラを完了させることで安定性を向上させます。
背景
ColdBootVisit と WKUIController の認証チャレンジや JavaScript アラート/確認パネルで、デリゲートが dealloc 後に WebKit がコールバックを発行すると、オプショナルチェーンが何もせずに抜け、必須の completionHandler が未呼び出しのまま例外が発生していました。実際のクラッシュレポートは completionHandler が呼び出されずに NSInternalInconsistencyException がスローされる形で報告され、ユーザーに致命的な影響を与えていました。
技術的な変更
全ての該当メソッドに guard let delegate を導入し、delegate が nil の場合は安全なデフォルトを即座に返すロジックへ置き換えました。これにより、WebKit が要求する completionHandler が必ず呼び出され、例外が回避されます。
Navigator.session(_:didReceiveAuthenticationChallenge:completionHandler:)
変更前:
delegate?.didReceiveAuthenticationChallenge(challenge, completionHandler: completionHandler)
変更後:
guard let delegate else {
completionHandler(.performDefaultHandling, nil)
return
}
delegate.didReceiveAuthenticationChallenge(challenge, completionHandler: completionHandler)
Session.visit(_:didReceiveAuthenticationChallenge:completionHandler:)
変更前:
delegate?.session(self, didReceiveAuthenticationChallenge: challenge, completionHandler: completionHandler)
変更後:
guard let delegate else {
completionHandler(.performDefaultHandling, nil)
return
}
delegate.session(self, didReceiveAuthenticationChallenge: challenge, completionHandler: completionHandler)
ColdBootVisit.webView(_:didReceive:completionHandler:)
変更前:
delegate?.visit(self, didReceiveAuthenticationChallenge: challenge, completionHandler: completionHandler)
変更後:
guard let delegate else {
completionHandler(.performDefaultHandling, nil)
return
}
delegate.visit(self, didReceiveAuthenticationChallenge: challenge, completionHandler: completionHandler)
WKUIController.runJavaScriptAlertPanelWithMessage(_:completionHandler:)
変更前:
delegate?.present(alert, animated: true)
変更後:
guard let delegate else {
completionHandler()
return
}
let alert = UIAlertController(title: message, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .default) { _ in
completionHandler()
})
delegate.present(alert, animated: true)
WKUIController.runJavaScriptConfirmPanelWithMessage(_:completionHandler:)
変更前:
delegate?.present(alert, animated: true)
変更後:
guard let delegate else {
completionHandler(false)
return
}
let alert = UIAlertController(title: message, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
completionHandler(true)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
completionHandler(false)
})
delegate.present(alert, animated: true)
設計判断
delegate がオプショナルである設計 を尊重しつつ、WebKit が要求する必須 completionHandler の呼び出し義務を明示的に保証する方針が採用されました。guard let delegate による早期リターンはコードの可読性を保ち、デフォルト動作(認証は .performDefaultHandling、確認は false、アラートは空呼び出し)を選択することで既存ロジックへの影響を最小限に抑えています。さらに、未発生の runJavaScriptAlertPanelWithMessage も同様に保護することで、同種の潜在的クラッシュを予防する余裕設計となっています。
まとめ
デリゲートが解放された後でも必須 completionHandler が確実に呼び出されるようガードを導入したことで、NSInternalInconsistencyException に起因するクラッシュを根本的に解消しました。最小限のコード追加で後方互換性と安全性を両立し、Hotwire‑Native‑iOS アプリ全体の安定性が向上します。