[初心者向け]swiftにおける値渡しの方法の一部をご紹介

# 環境
- PC: MacBook Pro (15-inch, 2018)
- Xcode: Version 10.1
- Swift: 4.2

# 対象の読者
- Swift初心者の方
- 値渡しの方法を学習したい方

# 執筆したきっかけ
自分は，2019年2月16日と17日に行われたハッカソン（[P2HACKS](https://p2hacks.c.fun.ac.jp)）に運営兼ヘルパーとして参加しました．
P2HACKSは，”チーム開発を経験する”を目的とした育成型ハッカソンです．そのため，参加者の多くが未経験者でした．これに対しヘルパーとして参加した結果，画面間の値渡しに苦戦していたチームが見られました．したがって，少しまとめてみてはどうだろうと思い執筆しました

# プロジェクトの用意
[GitHub](https://github.com/ARATAYOKOYAMA/SamplePassbyvalue)にあげてあります．
よろしければ，cloneしてください  

# 値渡しの方法

## 1. segueを使用して画面遷移&値渡し
```Swift:ViewController.swift
    @IBAction func byPerformSegue(_ sender: Any) {
        self.performSegue(withIdentifier: "toSegueViewController", sender: nil)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "toSegueViewController" {
            let nextVC = segue.destination as! SegueViewController
            nextVC.text = "fromViewController"
        }
    }
```
まずは，segueを使用したパターンです．segueはstoryboardにて，ドラックアンドドロップで設定することができるため，非常に簡単に実装することができると思います．詳しいsegueを使用したパターンの実装方法は，こちら[【iOS】画面遷移の基礎【Swift3.0】](https://qiita.com/fromage-blanc/items/b3cb0e7833a1d5659463)を参考にしてみてください．

個人的な考え方ですが，segueを使用したパターンの欠点として下記のことが考えられると思います
1. 画面遷移を行う関数を呼び出す箇所と，値渡しをする準備を行う関数が別の場所にある
2. segueのidが増えていくと，条件分岐が増える  

今回の様に簡単なアプリであれば問題ないと思うのですが，UIパーツが増えるなどしてアプリの規模が大きくなると，1で述べたように離れていることから可読性が低下すると考えています．
また，それぞれのsegueにidを振ることから，増えたら増えただけ条件分岐が深くなります．こちらについては，[[swift] Storyboardのsegue idを「かんたん・きれい・色あせない」方法で管理する](https://qiita.com/okamu_/items/beefea3c3d94207e1fbc)にて紹介されているenumを使用してswitch文を使用することをオススメします．

## 2. navigationControllerを使用して画面遷移&値渡し
```Swift:ViewController.swift
  @IBAction func byNavicationPush(_ sender: Any) {
      let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "navigationPushView") as! NavigationPushViewController
      nextVC.text = "fromViewController"
      self.navigationController?.pushViewController(nextVC, animated: true)
}
```
次にnavigationControllerを使用するパターンです．withIdentifierは，Storyboard上の各ViewControllerの「Storyboard ID」です．こちらは，自身がnavigationController下にいることが条件です．このパターンを使用すると，前の前の画面に値を渡したいor戻りたいという時，簡単に実装できます（navigationControllerはスタックでviewを管理しているため）．(参考サイトはこちらです．[[Swift3]2つ前の画面に戻る方法](https://yuu.1000quu.com/two_before_screen_return)  )
(初心者向けなので，Delegateやオブザーバー同期，Rxによるデータバインディングの話はしません)  
また，pushだけではなくpresentによる遷移も行えます

```Swift:
self.navigationController?.present(nextVC, animated: true, completion: nil)
```

## 3. presentを使用して画面遷移&値渡し
```Swift:ViewController.swift
  @IBAction func byPresent(_ sender: Any) {
        let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "presentView") as! PresentViewController
        nextVC.text = "fromViewController"
        self.present(nextVC, animated: true, completion: nil)
    }
}
```
次にpresentを使用するパターンです．こちらは，自身がnavigationController下にいる必要がありません．使い所としては，遷移先から新たに画面遷移を行わない時だと思います．ちなみに，遷移先を閉じるときは，下記のコードが必要になります．

```Swift:
self.dismiss(animated: true, completion: nil)
```

遷移先にUIButtonなどを配置して，適宜呼び出すと良いと思います．また，navigationControllerをしてpresentを行った場合は，下記のコードがオススメです

```Swift:
self.navigationController?.popViewController(animated: true)
```

dismissは，自身を破棄して前の画面に戻るための関数です．一方で，popViewControllerはpopを行って1つ前の画面に戻る関数です．storyboard上でnavigationControllerを設定した場合，自動的にbackボタンが出来上がります．そのbackボタンをタップすると，内部的にはpopViewControllerを呼んでいたような気がします（あんまり自信ないです）．

## 4. 異なるstoryboardにある画面に，navigationControllerを使用して画面遷移&値渡し
```Swift:ViewController.swift
  @IBAction func byNavigationPushForOtherStoryboard(_ sender: Any) {
        let storyboard: UIStoryboard = UIStoryboard(name: "Sub", bundle: nil)
        let nextVC = storyboard.instantiateViewController(withIdentifier: "otherStoryboard") as! OtherStoryboardViewController
        nextVC.text = "fromViewController"
        self.navigationController?.pushViewController(nextVC, animated: true)
    }
```
最後に，異なるstoryboardにある画面に対して，navigationControllerを使用するパターンです．
チーム開発，特にハッカソンなど短期間で一気に開発する場合，コンフリクトが起きることがあります．iOSアプリにおいては，storyboardによるコンフリクトが多いのではないでしょうか？
これに対するアドバイスとしては，ViewControllerとstoryboardの関係を1対1にすることです．そうすることで，開発者単位やブランチ単位で扱うstoryboardを最小限にできるはずです．
遷移先のViewControllerの「Is Initial View Controller」に，チェックが入っていることを確認してください（矢印のこと）．また，name: "Sub"のSubは，storyboardの名前です．

# 参考
- [【Swift 3】UIStoryboardSegue をコードから呼んで画面遷移](https://qiita.com/treastrain/items/8c298886cc8f3cf124f2)
- [【Swift】コードを使った二つの画面遷移の方法](https://qiita.com/zlia_7/items/e99b77372d8c0f38d98b)
- [【Swift4】次の画面にデータを引き継ぐ５つの方法【Xcode】](https://qiita.com/fromage-blanc/items/3ea2dfe97d4c2d6f0646)
- [【Swift/iOS】遷移元画面への戻り方](https://capibara1969.com/20181028/203/)
- [【Swift3】コードで画面遷移を行う方法](https://qiita.com/Simmon/items/6c3d6bcd6bfffbfd970d)