背景
StoryboardやXibをコードから呼び出す場合、その名前を文字列で指定する必要があります
文字列をベタ書きで指定するのは以下のデメリットがあります
- タイポする可能性が高まる
- タイポに気づけなければ無駄に時間を使ってしまう
- クラス名の更新時に呼び出しの文字列を変更し忘れて、StoryboardやXibが読み込めなくなる
- 読み込めなくなっていることをコンパイル時に気づけないので最悪そのままリリースしてバグになる
その対策として以下のようなExtensionを活用している方も多いと思います(私もその一人です)
extension NSObject {
static var className: String {
String(describing: self)
}
var className: String {
Self.className
}
}
参考: https://qiita.com/tattn/items/bdce2a589912b489cceb
この解決方法の課題
XibやStoryboardが紐付いているUIViewController/UIViewにGenericsパラメータがついている場合、正しく読み込めません
理由は、Genericsパラメータまでが className
に含まれてしまうため、StoryboardNameやnibNameとずれてしまうからです
class HogeViewController<T>: UIViewController {}
print(HogeViewController<String>.className)
// HogeViewController<String>
// 本当は HogeViewController の部分だけがほしい
解決方法
Genericsパラメータの手前までをnibNameとして切り出します
※サンプルはUIViewControllerだけですが、UIViewも同様のExtensionを生やせばOKです
extension UIViewController {
static var nibName: String {
let startIndex = className.startIndex
let endIndex = className.firstIndex(of: "<") ?? className.endIndex
return String(className[startIndex ..< endIndex])
}
}
上記ロジックはクラス名に <
が含まれていると意図しないところでnibName切れてしまいますが
私が試行錯誤した限りは <
をGenericsパラメータ以外の中途半端な位置に入れるとコンパイルエラーがでるので大丈夫だと思います
もし、抜け穴がありましたらコメントください
結果
Genericsパラメータがある場合
class HogeViewController<T>: UIViewController {}
print(HogeViewController<String>.className)
// HogeViewController<String>
print(HogeViewController<String>.nibName)
// HogeViewController
Genericsパラメータがない場合
class FugaViewController: UIViewController {}
print(FugaViewController.className)
// FugaViewController
print(FugaViewController.nibName)
// FugaViewController