🍲

表示上はnilなのに、if letのチェックを通過している

に公開

事象

iOSアプリが特定のAPI環境でのみクラッシュが発生しました。該当のコードは以下です。

func sample(cookie: HTTPCookie?) {
    //...省略
    if let cookie = cookie {
        let _ = HTTPCookie.requestHeaderFields(with: [cookie])//クラッシュ発生箇所
        //...省略
    }
    //...省略
}

原因特定

アーカイブされたアプリでは100%クラッシュが再現しましたが、同じコードをXcodeでビルドしてインストールしたアプリではクラッシュが発生しませんでした。Debugできないため、調査と修正検証が非常に難しい状態でした。

試行錯誤の末、Xcodeのスキーム設定を調整することで、ようやくビルド版でもクラッシュを再現できるようになりました。

その後、if let cookie = cookie の前で print(cookie) を実行したところ、出力はnilと表示されました。しかし、if let のチェック自体は通過していたため、その後のコードが実行されてクラッシュに至りました。

試しに以下のようにcookie.nameやcookie.domainなどのプロパティをチェックすると、クラッシュを防げました。

if let cookie = cookie,
   !cookie.name.isEmpty,
   !cookie.domain.isEmpty,
   !cookie.value.isEmpty {
    let _ = HTTPCookie.requestHeaderFields(with: [cookie])
}

表示上はnilなのに、if letのチェックを通過している。この挙動から、問題のCookieは「nilではないが、中身が無効なHTTPCookie」であることが分かりました。

解決方法

if let cookie = cookie が期待通りに動作しなかった理由は、HTTPCookie() が内部的に nil なプロパティを持つ無効なCookieオブジェクトを生成していた可能性が高いためです。これは完全な nil ではないため if let のチェックを通過してしまいますが、ヘッダーフィールドに変換しようとした際にクラッシュを引き起こします。

以下のようにコードを修正することで、クラッシュが発生しなくなりました。

  • HTTPCookie() のような空のCookieインスタンスは使用しない

  • Cookieが無効な可能性がある場合、明示的に各プロパティをチェックする

  • HTTPCookie.requestHeaderFields(with:) を使う際は、事前に中身の妥当性を必ず確認すること

SKIYAKI Tech Blog

Discussion