iPhone 13 mini 固有のカメラフォーカス問題と向き合った話
| 開発記録 | QRリーダー
タグ: #iOS #カメラ #デバッグ
「iPhone 13 mini だけ QR コードが読み取れない」という低評価レビューが続きました。手元の検証機ではぜんぜん再現しません。原因は本体のカメラのフォーカス挙動で、CocoaPods の post-install フックでライブラリにパッチを当てて直しました。冪等性で自分の罠にはまった話も書きます。
レビューの違和感
QR コードリーダーアプリに、まったく読み取れないという低評価レビューが続いた時期がありました。よくよく見ると、報告者の機種は決まって iPhone 13 mini です。手元の検証機(iPhone 12, 14, 15)ではぜんぜん再現しません。原因が分からないまま星だけが減っていく。しんどい時間でした。
同一コードで挙動が違う理由
調べていくと、iPhone 13 mini のリアカメラには癖がありました。特定のフォーカスモードでオートフォーカスが効きにくいのです。QR スキャンライブラリ(mobile_scanner / mlkit)が要求するモードと、13 mini の動作が噛み合っていませんでした。
手元に実機はありません。まずはレビューと Crashlytics から拾える情報、それにライブラリ側の issue を漁りました。たどり着いた回避策はこれです。フォーカスモードを continuousAutoFocus ではなく autoFocus にして、距離を指定する。
Pods 自動パッチという手段
問題は、ライブラリ本体のコードをいじる必要があったことです。フォーク版を作ってメンテし続けるのは、たぶん続きません。そこで CocoaPods の post-install フックでパッチを当てる方式にしました。
post_install do |installer|
# ライブラリの該当 Swift ファイルに修正を当てる
patch_camera_focus(installer)
end
パッチの内容は最小限です。該当箇所を正規表現で検出して、修正コードを差し込むだけ。
実際の Podfile はこんな雰囲気です。対象 Pod の Swift ファイルを File.read → gsub → File.write するだけ。冪等性を保つため、修正後の行にも再マッチするパターンを使います。
post_install do |installer|
installer.pods_project.targets.each do |t|
next unless t.name == 'mobile_scanner'
swift_file = File.join(installer.sandbox.root, 'mobile_scanner',
'ios', 'Classes', 'MobileScannerPlugin.swift')
next unless File.exist?(swift_file)
source = File.read(swift_file)
patched = source.gsub(
/focusMode = \.continuousAutoFocus[^\n]*\n/,
"focusMode = .autoFocus // patched for iPhone 13 mini\n"
)
File.write(swift_file, patched)
end
end
ライブラリ側の Swift 実装は、おおむね下記の形になります。autoFocus + 距離指定で、13 mini でも安定して読めるようになりました。
private func configureFocus(for device: AVCaptureDevice) throws {
try device.lockForConfiguration()
defer { device.unlockForConfiguration() }
if device.isFocusModeSupported(.autoFocus) {
device.focusMode = .autoFocus
}
if device.isLockingFocusWithCustomLensPositionSupported {
device.setFocusModeLocked(lensPosition: 0.4, completionHandler: nil)
}
if device.isAutoFocusRangeRestrictionSupported {
device.autoFocusRangeRestriction = .near
}
}
正規表現が引っかかる罠
最初に書いたパッチは、「修正前のコード」だけにマッチする正規表現でした。当然、pod install を 2 回走らせるとマッチしません。CI で壊れます(自分で仕込んだ罠に、自分で落ちました)。
修正後は「修正後のコードにも再マッチする」表現に変えました。これで何度 pod install しても冪等に動きます。地味ですけど、大事なところです。
ライト機能でも引っかかった
カメラの話のついでに。ライト機能(フラッシュ)でも別件で苦しみました。マルチカメラ対応デバイス(Pro 系の機種など)では、ライトを ON にするとスキャナが停止します。これも個別に対応しました。
実機を借りて確認する
最終的には iPhone 13 mini を友人から借りました。手元で挙動を確認できて、やっと安心しました。ありがたいです。リモートデバッグだけでは確証が持てません。デバイス依存の問題は、実機を一台確保するのが結局いちばん速いです。
まとめ
- 機種固有の問題は、まずレビューと Crashlytics の機種情報を見る
- ライブラリのパッチは Pods で自動化する。冪等性は正規表現で担保する
- どうしても再現しないなら、実機を借りる
「全機種で動くアプリ」を作るのは大変です。持っていない端末のことは、持っている人に聞くしかない。次に同じことが起きたら、まっさきに友人へ連絡します。