IBDesignable を使ったカスタムビュー使用時に発生するエラー原因を調べる

検証環境:
Xcode 11.3.1
Swift 5.1.3

Storyboard上で IBDesignable を使ったカスタムビューを貼り付けたところ、
error: IB Designables: Failed to render and update auto layout status for UIViewController (nyp-0u-XJD): The agent crashed
というエラーが出ました。
しかしこのメッセージだけではエラーの原因がわからないのでクラッシュログからエラー詳細を調べる方法をメモしておきます。

  • まずMac標準のコンソール.app(console.app)を起動します。
f:id:xyk:20200327114001p:plain
  • 検索窓にIBDesignablesAgentを入力してフィルタしておきます。

  • 対象の Storyboard を開き、メニューの Editor -> Refresh All Views を実行します。

f:id:xyk:20200327114348p:plain
  • するとコンソール.appのログに以下のようなクラッシュログが出力されます。

Saved crash report for IBDesignablesAgent-iOS[48380] version 11.3.1 (15706) to IBDesignablesAgent-iOS_2020-03-27-114733_xyk-mbp.crash

  • クラッシュレポートは ~/Library/Logs/DiagnosticReports/ ディレクトリに保存されるので、今回の場合なら
    ~/Library/Logs/DiagnosticReports/IBDesignablesAgent-iOS_2020-03-27-114733_xyk-mbp.crash
    ファイルを開きます。

  • このエラーログを見たところクラッシュの原因が特定できました。
    ログには
    Fatal error: Use of unimplemented initializer 'init(frame:)' for class 'MyButton'
    と出力されていました。
    IBDesignable を使ってる MyButtonクラスのコードを確認してみると、以下の coder が引数の初期化メソッドのみ定義していました。

required init?(coder: NSCoder) {
    super.init(coder: coder)
}

frame が引数の初期化メソッドを追加することでエラーが解消されました。

override init(frame: CGRect) {
    super.init(frame: frame)
}

required init?(coder: NSCoder) {
    super.init(coder: coder)
}

ちなみに両方とも書いてない場合はエラーになりません。
おそらく プレビュー時はランタイム時と違って coder ではなく frame 側の初期化メソッドのみ呼ばれるのが関係している気がします。
これにはちょっとハマってしましました。。

参考:

blog.kaltoun.cz