storyboard とのつきあい方

# はじめに
iOSアプリを作る際に storyboard はどうするのか？というのが悩みどころです。色々なところで議論されてますが storyboard について自分なりにまとめてみました。

# 方法
1. Main.storyboard に全てのせる
2. storyboard は使わない
3. Main.storyboard + xib
4. 機能ごとに storyboard を分ける
5. １画面１storyboard

## Main.storyboardに全てのせる
とりあえず何も考えずに Main.storyboard に全画面のせちゃえってやつです。

### やり方

<img width="812" alt="all" src="https://user-images.githubusercontent.com/34936885/59143550-87d81400-8a07-11e9-8fd8-9659ccb4d5ac.png">

### 利点

* 一目で画面遷移がわかる
* storyboard で色々設定できるのでコード量が減る

### 欠点

* 画面が増えてくると storyboard を開くのに時間がかかる
* 複数人開発の場合 storyboard でコンフリクトが頻発する

たまに40画面くらい全部のせとかいうプロジェクトもみましたが、間違って storyboard を開いてしまった時はイライラします。この方法は数画面程度の小さいアプリかつ開発者は1人の場合とかならやってもいいかなという感じです。

## storyboard は使わない
もう storyboard とか嫌や！消しちゃえってやつです。

### やり方

1. Main.storyboard を削除
2. プロジェクト設定 → Info → Main storyboard file base name の値削除
3. AppDelegate で rootViewController を設定
4. 画面のレイアウトは xib でやる

<img width="232" alt="2_file" src="https://user-images.githubusercontent.com/34936885/59143698-6f1c2e00-8a08-11e9-8732-374b2e8fa8ac.png">

rootViewController の設定

```swift:AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UINavigationController(rootViewController: FirstViewController())
        window?.makeKeyAndVisible()
        return true
    }
```

遷移処理

```swift:FirstViewController.swift
    @IBAction private func tappedSecond(_ sender: UIButton) {
        let vc = SecondViewController()
        self.navigationController?.pushViewController(vc, animated: true)
    }
    
    @IBAction private func tappedThird(_ sender: UIButton) {
        let vc = ThirdViewController()
        self.present(UINavigationController(rootViewController: vc), animated: true, completion: nil)
    }
```

### 利点

* コンフリクトが減る

### 欠点

* ぱっと見で画面遷移がどうなっているかわからない
* storyboard の恩恵を受けれないのでコード量は増える

途中参加したプロジェクトだと画面遷移を把握するのに多少苦労しますが、この方法はメンバとの話し合い次第でありかなと思います。（ちゃんと遷移図とかの資料があれば画面遷移も把握できます。資料があれば...）

storyboard は逆に色々設定でき過ぎてこの設定どこでやってんねん！てなる場合はオススメ。

## Main.storyboard + xib
storyboard は使いたい！けどレイアウト触るたびにコンフリクトは嫌や！ってやつです。

### やり方

1. Main.storyboard に ViewController をのせ View を剥がす。
2. xib を作成し、レイアウトを組む

<img width="654" alt="3_story" src="https://user-images.githubusercontent.com/34936885/59143803-c66ece00-8a09-11e9-829d-493d4ff4c8c0.png">

<img width="233" alt="3_file" src="https://user-images.githubusercontent.com/34936885/59143804-d686ad80-8a09-11e9-8e7a-01c315d95599.png">

遷移処理

```swift:FirstViewController.swift
    @IBAction private func tappedSecond(_ sender: UIButton) {
        self.performSegue(withIdentifier: "toSecond", sender: nil)
    }
    
    @IBAction private func tappedThird(_ sender: UIButton) {
        self.performSegue(withIdentifier: "toThird", sender: nil)
    }
```

### 利点

* Main.storyboard を見れば画面遷移がなんとなくわかる
* storyboard で色々設定できるのでコード量が減る（containerView とかは使えない）
* コンフリクトが減る

### 欠点

* Main.storyboard だけ見てもぱっと見でどこの画面かわからない
* xibでレイアウトを組むので containerView とか使えない
* 画面の生成方法に注意しないとバグる(下記参照)


下記のように画面生成されるとレイアウトはそのままですが storyboard の設定がなくなってしまいます。

```swift
let vc = FirstViewController()
```

この方法なら segue も使えるし、コンフリクトも起きにくいのでわりといい気がします。ただ、storyboard の恩恵が一部得られません。

## 機能ごとに storyboard を分ける
Main.storyboard に全部置くのはアレなんで機能ごとに分けちゃえってやつです。

### やり方

1. Main.storyboard を削除する。
2. ログイン機能、検索機能など機能ごとに storyboard を作成する
3. プロジェクト設定 → General → Deployment Info の Main Interface に初期表示の storyboard を設定する
4. storyboard 間の遷移は Storyboard Reference を使う

### 利点

* storyboard を見ればなんとなく遷移がわかる
* 機能ごとに分かれてるので見やすいといえば見やすい
* storyboard で色々設定できるのでコード量が減る
* コンフリクトが多少減る

### 欠点

* 遷移を追うには色々な storyboard を見ないといけない
* やっぱりコンフリクトが起きる
* どこの機能に入れればいいかわからない画面が出てきて storyboard が増える（終いには Other.storyboardとか出て肥大化する）
* 一部機能の画面が増えてくると storyboard を開くのに時間がかかる

この方法は正直中途半端な感じがして嫌いです。

## １画面１storyboard
xib とかナンセンス！完全上位互換の storyboard 使っちゃえってやつです。

### やり方

1. Main.storyboard を削除する。
2. 画面ごとに storyboard を作成する
3. プロジェクト設定 → General → Deployment Info の Main Interface に初期表示の storyboard を設定する
4. storyboard 間の遷移は Storyboard Reference を使う

<img width="1064" alt="4_story1" src="https://user-images.githubusercontent.com/34936885/59143930-6416cd00-8a0b-11e9-9607-7838f10f169f.png">

<img width="323" alt="4_story2" src="https://user-images.githubusercontent.com/34936885/59143960-94f70200-8a0b-11e9-86a4-ce4005dce77b.png">

<img width="594" alt="4_story3" src="https://user-images.githubusercontent.com/34936885/59143939-755fd980-8a0b-11e9-9b97-99aaa1f07b87.png">

<img width="214" alt="4_file" src="https://user-images.githubusercontent.com/34936885/59143918-4ea1a300-8a0b-11e9-9739-d7d233837815.png">

### 利点

* storyboard を見ればなんとなく遷移がわかる
* storyboard で色々設定できるのでコード量が減る
* コンフリクトが減る

### 欠点

* 遷移を追うには色々な storyboard を見ないといけない

この方法が一番いいんじゃないのかなと思います。segue も containerView も使えるし、コンフリクトも起きにくいです。ただこの方法で実装したことがないので思わぬ欠点があるかもしれません。

# xib より storyboard がいい点
今までは Main.storyboard + xib でやってたのですが１画面１storyboard の方がいいじゃないかなと思い、xib より storyboard がいい点についてまとめます。

## TableView の設定が色々できる
iPhone アプリの画面で多いのが tableView をのせただけの画面が多いと思いますが、xib の場合ぱっと見なんの画面かわかりません。

<img width="261" alt="table_xib" src="https://user-images.githubusercontent.com/34936885/59145087-4309a880-8a1a-11e9-8687-788562fab6aa.png">

storyboard の場合だとセルやヘッダーフッターをのせたりできるので一目でどの画面かわかります。（セルを他の画面でも使いまわしたい場合は xib でセルを作る方がいいと思いますが...）

<img width="264" alt="table_story" src="https://user-images.githubusercontent.com/34936885/59145106-72201a00-8a1a-11e9-89bb-15851337ef84.png">

## ContainerView が使える
複雑な画面で部品を他クラスで分割したい場合も xib の場合ぱっと見なんの画面かわかりません。
例えば上に tableView 下に　collectionView を置きたい場合。xib だと上下の View にカスタムクラスを設定してそれぞれ別で xib を作成するか、別で xib で ViewController を作成しコード上で上下の View に addSubView して childViewController の設定をしなければいけません。

<img width="259" alt="container_xib" src="https://user-images.githubusercontent.com/34936885/59145286-6d5c6580-8a1c-11e9-8867-40980e518c82.png">

storyboard の場合だと storyboard 上で設定できるし、見た目もわかりやすいです。

<img width="528" alt="container_story" src="https://user-images.githubusercontent.com/34936885/59145316-11dea780-8a1d-11e9-9df0-f4db358623e1.png">

# storyboard の 問題点
その他 storyboard 利用時の問題点についてです。

## id のタイポ問題
storyboard の segue や　Storyboard ID, セルの identifier などのタイポ問題。`performSegue(withIdentifier: "id", sender: nil)` などの利用時に id をタイポしていると実行するまでなかなか気づくのが難しいです。この問題は [R.swift](https://github.com/mac-cain13/R.swift) を使えば解決です。

## preparefor segue の肥大化
画面遷移時に値を渡したい場合 `func prepare(for segue: UIStoryboardSegue, sender: Any?)` で処理するので画面が増えるとどんどん処理が肥大化していき見通しが悪くなります。switch とかで処理を分岐して細かくメソッド分けるしかないかなと思います。

# さいごに
結論としては１画面１storyboard で R.swift を使うのがいいんじゃないかなと思います。この辺はまた Swift UI でガラッと変わるのかもしれませんが今のとこは１画面１storyboard 推しです。

いいんじゃないかなと思いますが、この方法やったことないので思わぬ欠点があるかもしれません。また、こんな方法もあるよってのがあればどなたか教えていただけたら幸いです。