Protocol oriented Storyboard

#はじめに
UIKit、特にStoryboardとかその辺全然Swiftyじゃないよね💩という話です。
俺はSwiftgen派だしな…って人も、protocolを使う上でのヒントが見つかるかもしれないので見ていただければ。
全部把握してるし別に…って人は回れ右です。貴方は1行でも多くコードを書くべきだ。

#結果

こんな感じのコードをprotocolを定義するだけで書けるようになります。
いずれもダウンキャストは必要ありません。

```swift
// Storyboard or Nib
let vc = ViewController.instantiate()
let view = View.instantiate()

// deque
let cell = tableView.dequeReusableCell(type: Cell.self, for: indexPath)

// segue
self.perform(segue: destination(ViewController.self), prepare: { (vc) in
   vc...
})
```

絶賛WIPなコードはこちら
https://github.com/tarunon/Instantiate

#ダウンキャストと曖昧さ
UIKit、特にStoryboardやNibの周りでは、しばしばダウンキャストが必要になるコードが出てきます。例えば、

```swift
let viewController = UIStoryboard(name: "ViewController", bundle: nil).instantiateInitialViewController() as! ViewController
let view = UINib(nibName: "View", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! View
```

のような。さて、このコードの何が邪悪かというと、曖昧さが高くて本来Swiftが持っているはずの言語の性能を殺している、コンパイラの性能を活かしきれていないところにあります。

```swift
//                                             ↓🤔                                                                 ↓🤔
let viewController = UIStoryboard(name: "ViewController", bundle: nil).instantiateInitialViewController() as! ViewController

```

わかりやすいように'🤔'を付けました。
例えばこの`ViewController`クラスをStoryboardから生成する場合、この`"ViewController"`というStoryboardNameは、実行時に変更され得るパラメータでしょうか?
恐らく、適切にViewControllerクラスを設計している貴兄らのプロジェクトにおいて答えは否、固定でしょう。
一つのクラスを複数のStoryboardから使うなんてヤケクソは、やるべきではありません。

そして、このようなコードを使ってる人もいるのでは。

```swift
extension ViewController {
  static let storyboardName: String { return "ViewController" }
  static func instantiate() -> ViewController {
    return UIStoryboard(name: storyboardName, bundle: nil).instantiateInitialViewController() as! ViewController
  }
}
```

ViewControllerを使う分には安心して`ViewController.instantiate()`を呼び出せる世界になりました。
しかし、この`extension`を毎回クラスに実装するのはイマイチです。
我々には`protocol`というブキがあります。

#Protocol Oriented...?
さて、先程のコードを抽象化して`protocol`として切り出してみましょう。

```swift
protocol StoryboardInstantiatable {
  static var storyboard: UIStoryboard { get }
  static func instantiate() -> Self
}
```

宣言はこれで十分でしょう。
そして実装は`extension`に書き出すことができます。

```swift
extension StoryboardInstantiatable where Self: UIViewController {
  static var storyboard: UIStoryboard {
    return UIStoryboard(name: NSStringFromClass(self).components(separatedBy: ".").last!, bundle: Bundle(for: self))
  }
  static func instantiate() -> Self {
    return storyboard.instantiateInitialViewController() as! Self
  }
}
```

やりました。`StoryboardInstantiatable`を`ViewController`に実装するだけで、`instantiate()`が使えるようになったと思います。
`ViewController`を`final class`にしないと怒られますが、まあ些細な問題でしょう。(まさかStoryboardでインスタンス化するViewContorllerをサブクラス化する人なんていませんよね!?)

しかし、もう少し、適切な抽象化について考えると、より良くなるかもしれません。

#Protocol Oriented
`instantiate()`というfunctionですが、これは自身以外のオブジェクトからインスタンスを生成する役割を持つものと定義できそうです。
例えば、Storyboardに限らず、Nibからインスタンスを生成する場合もありえます。
そうすると、`StoryboardInstantiatable`に直接`instantiate()`を生やすのではなく、

```swift
protocol Instantiatable {
  static func instantiate() -> Self
}

protocol StoryboardInstantiatable: Instantiatable {
  static var storyboard: UIStoryboard { get }
}
```
このように型を分割したほうが取り回しが効きそうです。

フィールドとしてのStoryboardを使いたいだけなのに、`instantiate()`が付いて来る、というのもイマイチです。(ホントはそれ以外の用途は無さそうですが)
Storyboardはともかく、Nibであれば、TableViewのReuseに使うという用途があり得ます。

そうすると、storyboardも分離しておいたほうが便利かもしれません。

```swift
protocol Instantiatable {
  static func instantiate() -> Self
}

protocol StoryboardType {
  static var storyboard: UIStoryboard { get }
}

protocol StoryboardInstantiatable: Instantiatable, StoryboardType {
}
```

良くなってきました。このように、UIKitが提供している不適切な抽象化を、一旦固めてしまってから、再度適切な抽象化をすると、概ねSwiftで気持ちよくコードが書ける環境になっていくと思います。

Segueについては前回書いたので、Reusable周りを書きたかったんですが、どうやらMP切れのようです。また今度。

#Conclusion
SwiftのProtocolの機能はとても強力で素晴らしい機能です。
しかし、現状のUIKitは素晴らしくありません。多くの不適切な抽象化によってコードが曖昧になり、Swift本来のコンパイラの能力を殺しています。
もしあなたが新規プロジェクトに携わっているなら、不適切な抽象化を一度無くしてしまって、再度自身で適切な抽象化(protocol作り)をしてみると良いと思います。
きっと、Swiftyなコードが書きやすくなっているはず！