iOS
Swift

UIStoryboardからViewControllerをロードするときにenumとprotocolとextensionを使ってカッコよくやる方法

More than 1 year has passed since last update.

よくあるやり方

let name = "Main"
let identifier = "ArticleViewController"
let storyboard = UIStoryboard(name: name, bundle: nil)
let viewController = storyboard.instantiateViewControllerWithIdentifier(identifier) as! ArticleViewController
  • このまま乱立すると、nameやidentifierの管理コストがひどい
  • as! でキャストするのはswiftでコード書いてると気持ち悪い感が。。

こういうふうに書けるようにできます

let storyboard = UIStoryboard(storyboard: .Main)
let viewController: ArticleViewController = storyboard.instantiateViewController()

定義方法

Storyboardのidentifierをプロパティとしてもつprotocolを定義します。

protocol StoryboardIdentifiable {
    static var storyboardIdentifier: String { get }
}

protocol extensionで初期値はUIViewControllerのクラス名がidentifierとなるように定義します。
そのため、Storyboard IDは定義しているViewControllerと同じ名前を使うようにしてください。(一番上の例だとStoryboard ID = "ArticleViewController" となります)

extension StoryboardIdentifiable where Self: UIViewController {
    static var storyboardIdentifier: String {
        return String(self)
    }
}

UIViewControllerにStoryboardIdentifiableを適合させます。

extension UIViewController: StoryboardIdentifiable { }

UIStoryboardを拡張します。

extension UIStoryboard {
    // アプリ内で作成されているStoryboardの名前をenumで定義します
    enum Storyboard: String {
        case Main
        case News
        case Favorite
    }

    // 上で定義したenumでinitできるように
    convenience init(storyboard: Storyboard, bundle: NSBundle? = nil) {
        self.init(name: storyboard.rawValue, bundle: bundle)
    }

    // StoryboardIdenfiableプロトコルに適合してるUIViewControllerのみ
    func instantiateViewController<T: UIViewController where T: StoryboardIdentifiable>() -> T {
        // ジェネリクスでプロトコルに適合してるのは保証される
        let optionalViewController = self.instantiateViewControllerWithIdentifier(T.storyboardIdentifier)

        guard let viewController = optionalViewController as? T else {
            fatalError("something error")
        }

        return viewController
    }
}

UIViewControllerの継承はいつも通りで使えるようになります。

class ArticleViewController: UIViewController { }

参考リンク

UIStoryboard: Safer with Enums, Protocol Extensions and Generics — Swift Programming — Medium