画面遷移でSegueを利用するメリット（Swift）

![Xcode-12](https://img.shields.io/badge/Xcode-12-brightgreen) ![Swift-5.3](https://img.shields.io/badge/Swift-5.3-brightgreen) ![iOS-14](https://img.shields.io/badge/iOS-14-brightgreen)

## はじめに
画面遷移で Segue はもうほとんど使ってないというのを聞いたので Segue を使う利点を捻り出してみました:dizzy_face:

（私は特に Segue 推奨派というわけではありません:innocent:）

そもそも Storyboard 使わない！という方法もあると思いますが今回はそこにはふれません。

## つくるもの
Root -> NavigationController -> First -> Second -> Third 

上記のように遷移して値を渡して Third から Root に戻るときにも値を渡す方法を考えてみます。

こんな感じ（遷移時に first とか second の文字列を渡してラベルに表示しています）

![segue](https://user-images.githubusercontent.com/34936885/118354574-a3aaa400-b5a6-11eb-9f54-62d75511b5cc.gif)

## Segueを使う場合
Segue を使う場合 storyboard はこんな感じです。

<img width="500" alt="segue" src="https://user-images.githubusercontent.com/34936885/118354389-d2744a80-b5a5-11eb-98d8-93add5d33ffd.png">

コードは Root がこんな感じになります。

```swift
final class RootViewController: UIViewController {

    @IBOutlet private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = ""
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let nav = segue.destination as? UINavigationController,
           let first = nav.viewControllers.first as? FirstViewController,
           let text = sender as? String {
            first.text = text
        }
    }

    override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, sender: Any?) -> Bool {
        if fromViewController is ThirdViewController {
            if let text = sender as? String {
                label.text = text
            }
        }
        return true
    }

    @IBAction private func showFirst(_ sender: Any) {
        performSegue(withIdentifier: "showFirst", sender: "root")
    }

    // Unwind Segue
    @IBAction private func backFromThird(segue: UIStoryboardSegue) {
    }
}
```

Third がこんな感じになります。

```swift
final class ThirdViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    @IBAction private func backToRoot(_ sender: Any) {
        performSegue(withIdentifier: "backToRoot", sender: (text ?? "") + ", third")
    }
}
```

<details><summary>:point_down:その他のコード:point_down:</summary><div>

```swift
final class FirstViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let second = segue.destination as? SecondViewController,
           let text = sender as? String {
            second.text = text
        }
    }

    @IBAction private func showSecond(_ sender: Any) {
        performSegue(withIdentifier: "showSecond", sender: (text ?? "") + ", first")
    }
}

final class SecondViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let third = segue.destination as? ThirdViewController,
           let text = sender as? String {
            third.text = text
        }
    }

    @IBAction private func showThird(_ sender: Any) {
        performSegue(withIdentifier: "showThird", sender: (text ?? "") + ", second")
    }
}
```
</div></details>

## Segueを使わない場合
Segue を使わない場合 storyboard はこんな感じです。

<img width="500" alt="no_segue" src="https://user-images.githubusercontent.com/34936885/118354395-d86a2b80-b5a5-11eb-8b40-a316c9a1bc2c.png">

コードは Root がこんな感じになります。

```swift
final class RootViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = ""
    }

    @IBAction private func showFirst(_ sender: Any) {
        if let nav = storyboard?.instantiateViewController(withIdentifier: "FirstNavigation") as? UINavigationController,
           let first = nav.viewControllers.first as? FirstViewController {
            first.text = "root"
            nav.modalPresentationStyle = .fullScreen
            present(nav, animated: true)
        }
    }
}
```

Third がこんな感じになります。

```swift
final class ThirdViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    @IBAction private func backToRoot(_ sender: Any) {
        if let root = navigationController?.presentingViewController as? RootViewController {
            root.label.text = (text ?? "") + ", third"
            root.dismiss(animated: true, completion: nil)
        }
    }
}
```

<details><summary>:point_down:その他のコード:point_down:</summary><div>

```swift
final class FirstViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    @IBAction private func showSecond(_ sender: Any) {
        if let second = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
            second.text = (text ?? "") + ", first"
            navigationController?.pushViewController(second, animated: true)
        }
    }
}

final class SecondViewController: UIViewController {

    var text: String?
    @IBOutlet private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = text
    }

    @IBAction private func showThird(_ sender: Any) {
        if let third = storyboard?.instantiateViewController(withIdentifier: "ThirdViewController") as? ThirdViewController {
            third.text = (text ?? "") + ", second"
            navigationController?.pushViewController(third, animated: true)
        }
    }
}
```
</div></details>

## 比較
2つを比較して Segue のメリットを捻出してみます:muscle:

1. storyboard を見たときに遷移がわかりやすい  
    <img width="500" alt="segue" src="https://user-images.githubusercontent.com/34936885/118354389-d2744a80-b5a5-11eb-98d8-93add5d33ffd.png">
    
    <img width="500" alt="no_segue" src="https://user-images.githubusercontent.com/34936885/118354395-d86a2b80-b5a5-11eb-8b40-a316c9a1bc2c.png">
1. Unwind Segue が使える 
Unwind Segue を使えば Third から Root に戻るときは Modal か Push か気にする必要がなくなります。
1. Third から Root に値を渡しやすい 
    Segue を利用しない場合、下記のように Root に値を渡すときに Modal か Push かによって処理が大きく変わります。Segue を利用すれば遷移方法に依存せず値を渡すことができます！

    ```swift
    @IBAction private func backToRoot(_ sender: Any) {
        if let root = navigationController?.presentingViewController as? RootViewController {
            root.label.text = (text ?? "") + ", third"
            root.dismiss(animated: true, completion: nil)
        }
    }
    ```

## おわりに
がんばって Segue の利点について書こうと思いましたが１画面からいろいろな画面に遷移する場合はどうしても `prepareForSegue` が太ってくるのでそこは難点:persevere:

id のタイポ問題とかは [R.swift](https://github.com/mac-cain13/R.swift) を使えば解決すると思います。

なんか他に利点ないかな:thinking: