StoryboardからUIViewControllerのインスタンス化をDRYに

# ワンStoryboardワンViewController制

複数のViewControllerを同じStoryboard内に定義すると不都合がいろいろ出てくる。その為、だいたいはViewController毎にStoryboardを分けるようにしている。

ViewControllerをインスタンス化するのに、だいたい次のようなコードを使うでしょう。

```swift
let sb = UIStoryboard(name: "SomeStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "SomeViewController")
self.present(vc, animated: true, completion: nil)
```

いちいち`"SomeStoryboard"`や`"SomeViewController"`を取り回すのがダルい、況してや名前を変えたりリファクタリングしたりする時を考えると本当に気が滅入る。

これをなんとかDRYにしたい。

# DRY化　第一弾

`Is Initial View Controller`にチェックを入れた状態にすると`UIStoryboard`の`instantiateInitialViewController()`が使えるようになる。更に、Storyboard名とViewController名とを同じにすることで管理し易くなる（整理整頓）。

先ずはStoryboardとViewControllerの名前を一致させ、`Is Initial View Controller`にチェックを入れておこう。

そんな状態であれば、さっきのコードが次のようになろう。

```swift
let sb = UIStoryboard(name: "SomeViewController", bundle: nil)
let vc = sb.instantiateViewController()
self.present(vc, animated: true, completion: nil)
```

文字列がまだ残ってしまっているが、少しスマートになったね。
とはいえやはり、文字列を直接扱いたくないよね。

# DRY化　第二弾

列挙型にするといった手法を選ぶ事もできる。その場合は次のようになろう。

```swift
enum Screen : String {
    case example = "SomeViewController"
}
let sb = UIStoryboard(name: Screen.example.rawValue, bundle: nil)
let vc = sb.instantiateViewController()
self.present(vc, animated: true, completion: nil)
```

これでは意味のある名前が付いた定数のようなものができた。
しかし、まだ文字列があるのは気に食わないし、謎の`rawValue`もぶっちゃけ目障り。

それにすべての画面名が静的に羅列されているのもイヤだな。
メンテナンスしたくない。

# 型名を動的に取得する

Swiftには型名を文字列で取得する方法があるんじゃない？

```
$ swift
Welcome to Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2). Type :help for assistance.
  1> class SomeViewController { }
  2> String(describing: SomeViewController.self)
$R0: String = "SomeViewController"
  3> ^D
```

おっ！できた！

# DRY化　第三弾

```swift
let sb = UIStoryboard(name: String(describing: SomeViewController.self), bundle: nil)
let vc = sb.instantiateViewController()
self.present(vc, animated: true, completion: nil)
```

よっしゃ！完全に文字列の直接的な扱いを撲滅できたね。

ただ、今度はいちいち`String(describing: Foobar.self)`を書かなくちゃならない。

汎用的な書き方でき・・・・・・・る！
Genericsが使えるじゃん。

# Super DRY

```swift
func getVcInstance<T: UIViewController>() -> T? {
    // ViewControllerの型名を取得する
    let name = String(describing: T.self)
    // ViewControllerの型の格納したBundleを取得する
    let bundle = Bundle(for: T.self)
    // Storyboardを取得し、TとしてViewControllerをインスタンス化してみる
    let storyboard = UIStoryboard(name: name, bundle: bundle)
    return storyboard.instantiateInitialViewController() as? T
}

guard let vc: SomeViewController = getVcInstance() else { return }
self.present(vc, animated: true, completion: nil)
```

もう、列挙する必要全く無し！ :tada: :tada: :tada: 
ありがたや～ありがたや～

これでコードが非常にスッキリする。

満足！

# Single View Appの例

新たに`ViewControllerOne.storyboard`と`ViewControllerTwo.storyboard`を作る。
それぞれのCustom Classに同じ名前のクラスを指定する。

Et voilà!

```swift:MainViewController.swift
// Main.storyboardのViewController

import UIKit

class ViewController: UIViewController {
    @IBAction func vcOneButtonTapped(_ sender: Any) {
        guard let vc: ViewControllerOne = getVcInstance() else { return }
        self.present(vc, animated: true, completion: nil)
    }

    @IBAction func vcTwoButtonTapped(_ sender: Any) {
        guard let vc: ViewControllerTwo = getVcInstance() else { return }
        self.present(vc, animated: true, completion: nil)
    }

    func getVcInstance<T: UIViewController>() -> T? {
        // ViewControllerの型名を取得する
        let name = String(describing: T.self)
        // ViewControllerの型の格納したBundleを取得する
        let bundle = Bundle(for: T.self)
        // Storyboardを取得し、TとしてViewControllerをインスタンス化してみる
        let storyboard = UIStoryboard(name: name, bundle: bundle)
        return storyboard.instantiateInitialViewController() as? T
    }
}
```

```swift:ViewControllerOne.swift
// ViewControllerOne.storyboardのViewController

import UIKit

class ViewControllerOne : UIViewController {
}
```

```swift:ViewControllerTwo.swift
// ViewControllerTwo.storyboardのViewController

import UIKit

class ViewControllerTwo : UIViewController {
}
```