iPad で ActionSheet を表示するときに popoverPresentationController を設定し忘れてクラッシュするのを防止する

こんにちは。

iPadOS で ActionSheet を使う際には popoverPresentationController を適切に設定しないとクラッシュしてしまいます。

しかし普段 iPhone で開発していると iPad での確認が漏れてうっかりクラッシュする不具合を作り込んでしまいがちです。

弊社が開発している feather でも同様のことが発生したことを受けて SwiftLint を使用して警告を出す手法を導入しましたので紹介します。

対処方法

SwiftLint に以下の Custom Rule を追加しました。

  action_sheet_warning:
    name: "Action sheets must define a source view and rect, or barButtonItem"
    message: "iPadのActionSheetはsourceView,RectかbarButtonItemを設定する必要があります(ex: sheet.popoverPresentationController?.assign(to: ancker))。設定したあとは swiftlint:disable:next action_sheet_warning を設定して警告を無効にしてください。"
    regex: "preferredStyle: (.actionSheet)"
    capture_group: 1
    severity: warning

sheet.popoverPresentationController?.assign(to: ancker)) というのは自分で作った便利な extension メソッドなので適宜読み替えてください。

これにより以下のようなコードを書いた場合、スクショのような警告が表示されます。

let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

SwiftLint の警告

これに対して適切に popoverPresentationController を設定したあとに // swiftlint:disable:next action_sheet_warning とコメントすれば以下のように警告が抑制されます。

警告修正

popoverPresentationController をちゃんと設定するかどうかは人間に任せているので Lint としては微妙ですが、 actionSheet を作った時点でちゃんと設定する必要があることを思い出させるような意図で警告を出しています。

他に考えたこと

SwiftLint で行う以外にも Method Swizzle で UIViewController の present をするとき popoverPresentationController が設定されているかチェックする方法などを検討してみましたが実行時にチェックする方法は

  • そもそも実行時にそのパスを通らないとチェックされないのでうっかりミスをチェックできない
  • iPad では存在せずに iPhone のみで存在する View もあるので一律にチェックするのは無意味

といった懸念があったため、静的解析時に警告を出してあとの判断はプログラマに任せるのが良いという方針にしました。

まとめ

うっかりミスで popoverPresentationController を設定し忘れて iPad でクラッシュしてしまう不具合を作ってしまうのを防ぐ方法を紹介しました。

SwiftLint の Custom Rule は正規表現で簡単に作れるのでこういった簡単なチェックを追加すると他のケースでも応用が効くと思います。