iosの開発を始めたあの日の僕に伝えたいこと。



# 0. 自己紹介
  entakuです。
  元々はSIerでWebエンジニア(JavaとかPHPとか)書いてました。
  その前はネットワークエンジニアとかやってました。
  今は株式会社LifeSportsでスポーツマッチングアプリ作ってます😀
  
  僕が本格的にios開発を始めたのは、今年初めからです。
  swiftアプリはUdemyなどで勉強してなんとなくわかるかなとは思っていたのですが、
  学習サイトをみながら作ることと、実際にアプリを作って行くことはかなり違いました。
  1年弱経って、1年前の自分に伝えたいことをまとめてみました。
  

# 1. ViewControllerのライフサイクルをつかもう！
  xcodeでアプリを作成すると最初にviewControllerが作成されています。
  viewControllerには処理の順番があらかじめ決まっており、「このタイミングで処理する」というのを意識しておく必要があります。

僕の場合は、viewWillAppearでナビゲーションバーを消したり
viewDidAppearでデータ取得して画面更新かけたりしますかね。

もちろん場合によってなので、こういうことができるんだ！ってことは知ってた方がいいかと。

```swift
// 初期表示時に必要な処理を設定します。
// 基本的な初期化はここで
override func viewDidLoad() {
    super.viewDidLoad()
    print("viewDidLoad")
}

// UI 部品を View へセットする場合はこちらをオーバーライドします。
// UIの処理はここで定義しなくてもいいですが、決めておくとわかりやすい！
override func loadView() {
    super.loadView()
    print("loadView")
}

// 画面に表示される直前に呼ばれます。
// viewDidLoadとは異なり毎回呼び出されます。
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    print("viewWillAppear")
}

// 画面に表示された直後に呼ばれます。
// viewDidLoadとは異なり毎回呼び出されます。
override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    print("viewDidAppear")
}

// 画面から非表示になる直前に呼ばれます。
// viewDidLoadとは異なり毎回呼び出されます。
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    print("viewWillDisappear")
}

// 画面から非表示になる直後に呼ばれます。
// viewDidLoadとは異なり毎回呼び出されます。
override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    print("viewDidDisappear")
}
```
UIViewControllerのライフサイクル
https://qiita.com/motokiee/items/0ca628b4cc74c8c5599d

# 2. Viewを知ろう！
iosで利用されるものにはいくつかの特徴的なViewがあります。
特定のUIには適さないViewがあるのでとても注意が必要です。

## 2-1. TableViewの場合


例えば、TableViewなんかは下記の特性があります。

TableView
  ⭕️同じものを同じ大きさで繰り返して表示するのが得意
  ❌異なる情報を異なる大きさで表示するのは不得意

![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/7609572c-1e34-01b5-19f3-aa8bdc9ca63d.png)

TableView
https://developer.apple.com/documentation/uikit/uitableview

## 2-2. StackViewの場合
部品を並べて簡単にAutoLayoutしたいときに使うのがStackViewです。
    
  StackViewを賢く使ってらくちんAutoLayout
  https://qiita.com/yucovin/items/ff58fcbd60ca81de77cb
  StackView Apple Developer
  https://developer.apple.com/documentation/uikit/uistackview
  
  
下記はStackViewで作ったコメント入力用のviewです。
部品の表示非表示をするだけで、StackViewで表示を変えています。
  
![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/ce42e8b8-a0d1-2bc1-8b8b-9a573bcbb9bc.png)

初期状態
![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/68fac27b-4872-a4a2-8858-4f305e0d427b.png)

コメント入力時
![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/a32c93a3-8ba9-8ed1-085b-1edad7bb93dc.png)


以上のようにviewの特性を知って「ここはこのviewで作るといい感じに作れそう」
とイメージすることで、実装にスムーズに入れます

# 3. delegateとdatasourceを知ろう！
 tableViewやCollectionViewなどではあらかじめ用意されている定義delegate/datasourceがあります。
　このあらかじめ定義された部分に値や処理を書くことで、あとはtableViewやCollectionViewが処理を実行してくれます。
 

```swift
extension viewController: UITableViewDataSource {
  
    // セクションの数を指定する
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    // セクション内のCellの数を指定する
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return events[section].count
    }

    //Sectionに表示するheaderViewを定義する
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = TopParticipateRecommendSectionHeaderView.init(reuseIdentifier: sections[section])
        return headerView
    }

    

    // Tableに表示するCellを定義する
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueCell(TopParticipateWaitingCell.self, indexPath)

        return cell
    }
}


extension viewController: UITableViewDelegate {
  
    // cellをクリックした時の動作を定義する
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    }
}
```

この他にもたくさんのdelegateメソッドがあり、
多くの処理は則って書くだけで実現することができます。


https://developer.apple.com/documentation/uikit/uitableviewdelegate


# 4. 非同期で実行する方法をしろう！
   
   アプリ開発では「この処理が終わってから次の処理に行きたい」ということがよくあります
   多くの非同期処理があるのですが、ここではClosureについて言及します。
   
## 4-1. 子ViewController側で戻る処理の定義
   まずは子供の定義です。
   ここでは「施設を選択したら、親に施設情報を返す」ということを行なっています。
   
   子ViewController

   ```swift

      // Closure
      var selectPlaceCompletionHandler: ((_ facility: Facility?) -> Void)?

      // ボタンクリック時の処理
      @IBAction fileprivate func confirmFacility(_ sender: UIButton) {

          self.dismiss(animated: true) {
              // 施設の情報を親に返す
              self.selectPlaceCompletionHandler?(self.facility)
          }
      }
   ```
## 4-2.  親ViewController側で帰ってきた処理の定義
    
親側で子から戻ってきた後の処理を書きます。
処理の順番としては
親から子供を表示->子供の中の処理->親で定義している処理の実行
となり、親が子供の処理を待つことができます。
    
親ViewController   

```swift
// 子ViewControllerの定義
let vc = FacilitiesListViewController()
vc.selectPlaceCompletionHandler = { (facility) in
      //帰ってきた施設情報をここで使えます
}
let navigation = UINavigationController(rootViewController: vc)
// 子ViewControllerを表示
present(navigation, animated: true, completion: nil)
```
   

   
   

# 5. デバッグをしよう！
  実際自分の書いたコードが意図通りに動いているか？を確かめたい時はデバッグしましょう。
  

1.　logのデバッグ
  
```swift
debugPrint('処理が実行された！')
```

2.　UIのデバッグ
最終的に「意図した画面表示されること」を確認する場合がアプリ開発では多くなります。
      そのため、UIのデバッグをして「意図した画面表示」を確認します

アプリ実行中に下記のボタンを押します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/488309d7-5767-d41d-5506-683ff9903274.png)
するとviewが階層になって表示されます


![image.png](https://qiita-image-store.s3.amazonaws.com/0/55864/5be4b58a-9d24-5598-f518-06fff25d7430.png)

ここで対象のviewをクリックすると、viewにどんな定義がされているか見ることができます。

  


# 6. 色んなswiftの処理の書き方を知ろう！
  swift独自じゃないものもありますが、僕がswiftでよく使うと思われる書き方まとめてみました。
  
  
  
## 6-1.   Struct
全体で使いたい値や処理を置く時に格納する。
APIの定義や定数,Modelの宣言に使用する


Modelの宣言例

```swift
struct UserEntity: Decodable {
    let id: Int
    let fullname: String
    let picture: String
}
```


Swift さくっと確認したい基礎文法 [Struct(構造体)]
https://qiita.com/yuinchirn/items/98b568d595650eca3334

## 6-2.   enum
全体のパターンがわかってる時に使うものです。
TableViewやCollectionViewのセクションの数を定義する時によく使います。
その他自社ではスポーツのレベルを定義したい時に使ってますね。
    
    
https://qiita.com/hachinobu/items/392c96820588d1c03b0c
swift4.2からallCases使えるの嬉しい😊
    
   ```swift
  enum SportsLevel: Int {
    case none = 0
    case elementary
    case intermediate
    case advanced
    
    var title: String {
        switch self {
        case .none: return "未設定"
        case .elementary: return "初級"
        case .intermediate: return "中級"
        case .advanced: return "上級"
        }
    }
  }
  ```
  
  
## 6-3.   guard let / if let などのnilチェック
  
  nilチェックに使います
  swift以外ではみたことない(僕が知らないだけかも。)
  nilはアプリ開発の敵なので、'nilじゃない時だけ処理する' を実現する時に使います。
  
  
  
  ```swift
    guard let hoge = hoge else {return}  // hogeがnullだとこれ以降の処理は実行しない

    if let hoge = hoge {
      // hogeがnullじゃない時だけここが実行できる
    }
  ```
  
  
  


# 7. わからないことは質問しよう！
  今は質問しようと思えばどこにでも質問できます
  
## 1. xcodeやswiftの使い方
  xcodeやswiftの使い方は下記の3つがstandardかと。
  teratail
  https://teratail.com/
  stackoverflow
  https://stackoverflow.com/
  apple developer forum
  https://developer.apple.com/devforums/
  
  
  ライブラリなどはgithubやslackコミュニティーがある場合が多いので質問してみましょう。  
  



  以前Realmの使い方がわからなかった時にRealmのSlackコミュニティに質問してみました。
  stackoverflowやgithubでもissueなどで質問しています。
  

  Slackコミュニティに質問した時の話
  https://qiita.com/entaku19890818/items/f9f75cacbf5209d59207
  
  
# 8. デザインに意見しよう！
   デザインが無いと、アプリが作れません。(当たり前)
   基本的にはデザイン通りにつくるのですが、構造上難しいものだったり、こんな場合どうする？ってのがあります。
   そんな時はデザイナーと議論しましょう
   
## 8-1. Viewの特性から外れることをリスクとして伝えよう
    
　"Viewの特性を知ろう！" に出てきたのですが、iosのViewの特性上どうしても難しいUIというのが出てきます。
　ここはちゃんとデザイナーなど他の人を巻き込んで下記のことをするのが必要だと思っています。
　* なぜ難しいのかを説明する
　* なぜこのUIにするのか意図を確認する
　* 意図に対して代替え案を提示する
          
         
         
## 8-2. 動作があった時や端末が大きく小さくなった時の動きのイメージを確認しよう


       
あとはこの記事がとてもとてもわかりやすい。(正直これ読んどけばOK)
    
フロントエンドエンジニアから、デザイナーさんに意識してほしい10のこと
https://note.mu/pittan/n/n5789d09c5575

  

# 9. APIに意見しよう！
  APIが無いと、(だいたいの)アプリが作れません!()
  アプリ的には「こんな感じでAPIきて欲しいっ！」っていうものがあります。
  
  例えば、あるAPIでuserが返ってくる値が違うとき...


```json
"user": {
  "id": 1,
  "name": "test",
  "address": "東京都",
  "picture": "https://XXXX",
}
```

```json
"user": {
  "id": 1,
  "name": "test",
  "address": "東京都"
}
```

こんなときswift側では以下のようにModelを定義しています。

```swift
    let id:Int
    let name:String    
    let address:String
    let picture:String?  // いないデータもいるから、optionalで定義。
```

これくらいならあとでif let するだけでいいんで問題なさそうですが、
毎回毎回userの中身が異なると、何回if letすりゃーいいんだー！！となりかなり開発しずらいです...
swift的には基本「APIのresponseの中身は変えて欲しくない」のです。
この部分はAPI担当と議論して、どうしてもという場合だけにした方がいいです。






# 10. まとめ

以上つらつらと自分の経験からわかっていた方がいいと思ったことをまとめてみました。
これ以外にもこれはわかってた方がいいよ！だったり、
いやここわかんなかったよ！
ってとこがあればコメントいただければできる限り書きます。

iosの開発始めた時、どんな時に使うのかとかどんな風に考えるのかなど
公式サイトや検索結果ではなかなか出てこない知見が少なかったことに苦労しました。

こんな風に少しずつ自分の型を作っていければと思って今回まとめました😄

どこかのiosアプリ開発者の役に立てば幸いです🤗