チームでiOSアプリを初めて制作した話！！

みなさん初めまして！！Swiftを絶賛勉強中のりゅーちゃんです。関西の学生団体Volareに所属しています！
Swift学習して半年ほど経過し、団体内で開発しているアプリの制作が一旦落ち着いたので記事にしようと思いました！
今回はアプリのまとめ記事になりますが技術的にハマったことなども随時更新していきます:bicyclist:

#何を作っているの？
簡単にデモ動画を添えてアプリの説明をしていきたいと思います！!

アプリ作成の経緯なんですが団体内でアイディアソンをおこない、みんなで作りたいものを決めました。。。
そんな中で選ばれたのがこの案です！（スライドショーの貼り方が分からないので理解しにくくなってしまいますが書いて説明していきます:cry:）

アプリ作成においてメインのターゲットは私達のような一人暮らしの大学生です:dark_sunglasses:
アプリの決定した経緯については、
自炊をする際に献立を考えるのが面倒であるが、既存のアプリは冷蔵庫にあるものを参照して検索をかけている
→既存のもののターゲットは家族をもつ人向けである

好き。嫌いを入力して自動的に献立を提示してくれるアプリがあると便利じゃんと感じた！
→基本的に作り置きはあまりしない、好きなものを好きな時に作りたい（冷蔵庫に食材があまり入っていない:ghost:）

このような要点から作ることを決めました！（コンセプトが同じものがないなら作ってしまおう:relaxed:）
需要があればここについて詳しく記述していきます！！

デモ動画↓
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">なんとか形にすることができました！！<br>あとは細かいところをつめてリリース😇 <a href="https://t.co/QfcA3gZAKq">pic.twitter.com/QfcA3gZAKq</a></p>&mdash; 奥田りゅーや@クソザコイモムシ (@hd1J9rVe3xVFeU3) <a href="https://twitter.com/hd1J9rVe3xVFeU3/status/1199152891379908608?ref_src=twsrc%5Etfw">November 26, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#チーム構成、機能実装について 
サーバーサイド、デザイン、iOSの領域に別れて開発を進めていきました！
####サーバサイド機能実装について(後に詳しい機能について追記します:bow_tone1:)
- ユーザ認証機能
- デフォルトの食材一覧の設定
- 好き、嫌いの値による項目のランダム出力

####iOSm機能実装
- Alamofireを使用したAPI通信（get,put,post）
- MVC設計
- コンフリクトが生じないようにXibを使い画面を作成する
- デザインに準拠したアプリの画面作成（delegateの理解）

ハマったものは別の記事に詳細を載せますがAPI通信部分についてはコードを提示しておきます（技術記事だし:eyes:）

######:eyeglasses:サインイン、サインアップについて:eyeglasses:

######始めにViewControllerから見ていきましょう！！

```swift:RootViewController.swift

import UIKit

final class RootViewController: UIViewController {
    static func make() -> RootViewController {
        let viewController = RootViewController()
        return viewController
    }
    
    private var model = RootViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        if  UserData.ID != nil {
            signInUser()
        } else {
            signUpUser()
        }
    }
    
//UUIDを生成→UserDefaults内に保存→UUIDを取り出し、Modelに処理を委託→token取得
    private func signUpUser() {
        let uuid = UUID().uuidString
        UserData.ID = uuid
        model.createUser(uuid: uuid, handler: { result in
            UserData.token = result.token
            self.showHomeViewController()
        })
    }
    
//tokenの取得→同じユーザーかの確認
    private func signInUser() {
        guard let uuid = UserData.ID else { return }
        model.loginUser(uuid: uuid, handler: { result in
            UserData.token = result.token
            self.showHomeViewController()
        })
    }
```
サインイン、サインアップの処理についてはViewの方でUUIDの保存、取得、tokenの保存、取得を行っています！
tokenが一致した時、取得に画面が遷移する簡単な処理を実行しています！:kissing_heart:

######お次はModelです！！

```swift:RootViewModel.swift
import Foundation
import Alamofire

class RootViewModel {
    func createUser(uuid: String, handler: @escaping (User) -> Void) {
        let params: [String: Any] = [
            "uuid": uuid
        ]
        
        let url = URL(string: "＊＊＊＊＊＊＊＊＊＊＊/signup/")!
        
        AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default)
            
            .validate(statusCode: 200..<300)
            .responseJSON{ response in
                switch response.result {
                case .success( _):
                    guard let data = response.data else { return }
                    guard let token = try? JSONDecoder().decode(User.self, from: data) else { return }
                    handler(token)
                case .failure(let error):
                    print(error)
                }
        }
    }
}

extension RootViewModel {
    func loginUser(uuid: String, handler: @escaping (User) -> Void) {
        let params: [String: Any] = [
            "uuid": uuid
        ]
        
        let url = URL(string: "＊＊＊＊＊＊＊＊＊＊＊/signin/")!
        
        AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default)
            
            .validate(statusCode: 200..<300)
            .responseJSON{ response in
                switch response.result {
                case .success( _):
                    guard let data = response.data else { return }
                    guard let token = try? JSONDecoder().decode(User.self, from: data) else { return }
                    handler(token)
                case .failure(let error):
                    print(error)
                }
        }
    }
}
```
ModelについてはAlamofireを使用してViewから受け取ったUUIDを使いサーバーさんと通信しtokenを取得しています！

######最後にCodableまわりです！
```swift:APIResponse.swift
import Foundation

struct User: Codable {
    let token: String
}
```
以上になります！！
今回に関してはクロージャー？？、非同期？？など理解が足りないためにハマった箇所がいくつもありましたがその部分に関しては別記事にまとめ発信していきます！！


##最後に
初めてアプリを作成、チームでの開発ということもあり開発の工数が予想以上に膨らんでしまいました。。。
苦しい期間が長かったですが実際に動いてくれているのを見ると我が子のようで可愛いですっ:relaxed:

今回はiOSを担当しましたが今後はサーバーサイド の勉強を進めていきたいと感じています！！
最後まで読んでくださり、ありがとうございました:bow_tone1: