【Server-side-Swift】【Vapor】iOSアプリエンジニアが挑戦する初めてのサーバーサイド【勉強会レポ】

# はじめにの前に(概要/対象読者)
### 概要
- SwiftでサーバーサイドAPIの開発ができる <br>
　　-> サーバーサイドの知識があまりなくても開発できちゃう
- Swiftすごい、Vaoprすごい

### 対象読者
- Swiftの基本的なコーディングを経験した人
- サーバーサイド知識はあまりないが(Swiftを用いた)サーバーサイドの構築に興味がある人

※今回の勉強会で使用したコードは以下に置いています。Vaporで生成されたコードをちょっといじっただけですが。
https://github.com/s-kamada/Swift-server

あくまでレポなので勉強会の内容に合わせて記述します。

# はじめに
今年の3月に大学を卒業し、4月からアプリを作る会社に入社した者です。
まだ研修期間中なのですが、研修では所定の要件に沿ったiOSアプリとAndroidアプリを製作しています。

先日研修のiOSアプリの製作が終了し、「Swiftの知見をさらに深めたい！」ということでこちらの勉強会に参加してきました。
[iOSアプリエンジニアが挑戦する初めてのサーバーサイド(connpassリンク)](https://ueqareer.connpass.com/event/128823/)
[スライド(SlideShare)](https://www.slideshare.net/IkoNakari/server-side-swift-vapor-146597737)
[![スライド](https://image.slidesharecdn.com/serversideswift-190519114516/95/slide-1-1024.jpg)](https://www.slideshare.net/IkoNakari/server-side-swift-vapor-146597737)

大学時代にエンジニア志望の仲間として緒に勉強していた友人が講師役の勉強会です。
生粋のiOS/Swiftエンジニアですが普段は睡眠とAIを掛け合わせた研究をしているんだとか。

# Swiftでサーバーサイドを作れるらしい
## Swiftで書くと何がいいの？
- 早い 

> Swiftは４秒で終了しました。 Javaは4.3秒と、ほぼ同じぐらいです。このテストにおけるSwiftとJavaの性能はほぼ同じですね。Node.jsを見てみましょう。15.8秒かかっています。SwiftやJavaの約4倍ですね。Rubyはどうでしょう。とても遅いです。[^1]

- 省メモリ 

> RSS（ベンチマークを実行するのに必要な物理メモリ領域）のベンチマークを見てみると、Swiftでは15MBで、Javaでは32MBです。必要なメモリはJavaの半分なのに、同じ性能です。Node.jsはJavaよりは若干少ないですが、Swiftよりずっと多いです。Rubyはたくさん必要ですね。[^1]

## なんでVaporなの？
### Server-side-swift のフレームワークは数多あるけれど
- perfect
- Swifton
- Slimane
- Express
- HTTPSwiftServor
- Kitura
- Vapor    etc......

主流はKitura、Vapor。
海外ではVaporの方がファンが多く、サーバーサイドの知識があまりない人はVaporの方がいい、ただしVaporの方がSwiftのアップデートへの対応が早いが、開発者もそれについていくためのコストがかかる、など長短あるようです。[^2]

# Vaporを使う
※ Homebrew、Swift/Xcode(4.1.0以上)がインストールされている前提です
## 構築
### インストール

```
$ brew install vapor/tap/vapor
```

### プロジェクト作成
```
$ cd [プロジェクトを置きたいディレクトリ]
$ vapor new [プロジェクト名]
```
イカしたロゴとともにプロジェクトが生成されます
![スクリーンショット 2019-05-22 14.09.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/e0c97c3d-c462-2d2c-3fc4-57221778ba79.png)

### プロジェクトを開く(Xcode)
すこし時間がかかります

```
$ vapor xcode
Generating Xcode Project...
Open Xcode project?
y/n> y
Opening Xcode project...
```

### プロジェクト構成
![スクリーンショット 2019-05-22 14.51.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/09db7013-3a50-196f-2f85-907993a5b014.png)
基本的に編集するのは`App`フォルダ内のファイルです
`Controllers`はコントローラ、`Models`はモデルを入れるフォルダ、など見た通りの命名がされています。

## コーディング(抜粋)
自動生成されたものだけも十分動きますが、ちょっといじります。
今回は「Todo管理アプリ」のサーバーサイドAPIを作成する前提で編集します。

### モデル
```TodoModel.swift
final class TodoModel: SQLiteModel {
    /// The unique identifier for this `Todo`.
    var id: Int?
    /// A title describing what this `Todo` entails.
    var title: String
    var isFinished: Bool

    /// Creates a new `Todo`.
	init(id: Int? = nil, title: String, isFinished: Bool) {
        self.id = id
        self.title = title
		self.isFinished = isFinished
    }
}
```
データモデルです。マイグレーション(自動的にデータベースの更新・管理を行う)などの`extension`も実装できます。

## コントローラ
```TodoController.swift
final class TodoController{

    /*中略*/

    //新しいTodoを生成する
    func create(_ req: Request) throws -> Future<Todo> {
        return try req.content.decode(Todo.self).flatMap { todo in
            return todo.save(on: req)
        }
    }

    //すでに登録されているデータを更新する
    func update(_ req: Request) throws -> Future<Todo> {
        return try flatMap(to: Todo.self, req.parameters.next(Todo.self), req.content.decode(Todo.self)) { todo, updateTodo in
    todo.title = updateTodo.title
    todo.isFinished = updateTodo.isFinished
    return todo.save(on: req)
        }
    }
}
```
データの読み込み、生成、削除の操作は自動で生成されますが、不足しているので更新処理を追記しました。

### ルーティング
```routes.swift
public func routes(_ router: Router) throws {
    // そのエンドポイントのルートにアクセスした場合
    // Basic "It works" example
    router.get { req in
        return "It works!"
    }
    
    // http://(エンドポイント)/hello/world　にアクセスした場合
    // Basic "Hello, world!" example
    router.get("hello","world") { req in
        return "Hello, world!"
    }

    // http://(エンドポイント)/todos　に対してAPIを叩く場合
    // Example of configuring a controller
    let todoController = TodoController()
    router.get("todos", use: todoController.index)
}
```
ルーティングを定義するファイルです。

## ビルドしてサーバーを立てる
左上のバーを`Run > My Mac`と設定してビルドします。この場合は`localhost:8080`にサーバーが立ちます。
ターミナル上でもビルドが可能ですがXcode上でビルドするよりも時間がかかるようです。

```
$ vapor build
$ vapor run
```

## 動いたか確認してみる
ブラウザで`localhost:8080`に移動してみると...
<img width="299" alt="スクリーンショット 2019-05-22 15.45.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/187fd4db-324b-5b77-7636-790255073dd6.png">
動いてるみたいですね :dancer: 
他のAPIも叩いてみます。ここからはAPIツールの[Postman](https://www.getpostman.com/)を使用します

- 追加
<img width="856" alt="スクリーンショット 2019-05-22 15.50.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/4a95edf4-1369-39db-2961-758d41afdfbf.png">

- 一覧取得
<img width="860" alt="スクリーンショット 2019-05-22 15.51.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/db725a50-7218-ebe9-cbf9-208645d7e057.png">
レスポンスが帰ってきているのがわかりますね :dancer: 

# まとめ
- サーバーサイドAPIをSwiftだけで簡単に作れる。やったね！ :dancer: 
- 課題(今回の勉強会では触れなかったこと)
    - DBとの連携が必要(今回はしていません。連携の設定をしないとXcodeを落とした際にデータがリセットされます)
    - マイグレーションの方法
    - デプロイ方法


[^1]: [Try! Swift NYCの講演](https://academy.realm.io/jp/posts/tryswift-chris-robert-end-to-end-application-development-swift-backend/)より。現在は少し記録が更新されているようですが、やはりSwiftは早いかつ省メモリなようです[(The Benchmarks Game)](https://benchmarksgame-team.pages.debian.net/benchmarksgame/measurements/swift.html)

[^2]: [ここ](https://www.raywenderlich.com/1323930-vapor-vs-kitura-choosing-a-server-side-swift-framework)や[Hacking with Swift](https://www.hackingwithswift.com/articles/73/server-side-swift-kitura-vs-vapor)など、KituraとVaporを比較考察している記事が多いです。