Swiftでサーバーサイドに入門してみる(Vapor編)

2022.12.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

SwiftはAppleデバイスのソフトウェア開発で有名な言語ではありますが、Swiftでもサーバーサイドを開発出来るとのことでちょっと試してみることにしました。

はじめに

サーバーサイドSwiftで有名なフレームワークVaporのドキュメントからHello, world!実行までとJSONを返すところまでの初歩的な記事になります。

環境

  • Xcode 12.2
  • Swift 5.7.2
  • Vapor 4

Vapor 4 には Swift 5.2 以降が必要です。

インストール

Xcodeはインストールされている前提で進めていきます。

HomeBrewのインストール

Vapor ToolboxはHomebrew経由で配布される為、Homebrewをインストールする必要があります。既にインストール済みの場合はこの手順は必要ありません。

Homebrewのページにてインストール手順の記載があり、ターミナルに下記を入力し実行することでインストールが行えます。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Vaporのインストール

Homebrewがインストールされた(されている)場合は、ターミナルに下記を入力してVaporをインストールします。

brew install vapor

ここまででVapor Toolboxがインストール出来ました。

プロジェクトの作成

作成する場所はどこでも良いですが、今回はデスクトップ上に作成する為、ターミナルを使用してデスクトップを現在のディレクトリに変更します。

cd ~/desktop

同じくターミナル上で、Vaporプロジェクトを新規で作成します。hello-worldの箇所は任意の名前で問題ないです。

vapor new hello-world

vapor newを実行すると、作成が始まります。

Fluentフレームワークを使用するか問われます。

Cloning template...
name: hello-world
Would you like to use Fluent? (--fluent/--no-fluent)
y/n>

今回は使用しない為nを入力します。

すると、次にLeafフレームワークを使用するか問われます。

Would you like to use Leaf? (--leaf/--no-leaf)
y/n>

同じく今回は使用しない為、nを入力します。

アスキーアート的なものがターミナル上に表示されたら作成は完了です。

プロジェクトが作成されたので、Package.swiftをクリックしてプロジェクトを立ち上げます。

フォルダ構造

プロジェクトのフォルダ構造はこのようになっています。

.
├── Public
├── Sources
│   ├── App
│   │   ├── Controllers
│   │   ├── Migrations
│   │   ├── Models
│   │   ├── configure.swift
│   │   └── routes.swift
│   └── Run
│       └── main.swift
├── Tests
│   └── AppTests
└── Package.swift

引用: Folder Structure

Hello, world

Vaporのプロジェクトを作成しました。デフォルトの状態で既にローカルホスト上でサーバーを起動することが出来ます。

起動するデバイスをMy Macに設定し、ランを実行します。

ランに成功すると、デバッグコンソールにローカルホスト上でサーバーが起動したことが出力されています。

[ WARNING ] No custom working directory set for this scheme, using /Users/your-user-name/Library/Developer/Xcode/DerivedData/hello-world-fhdbhamkzhbtycannplsuwagehin/Build/Products/Debug
[ NOTICE ] Server starting on http://127.0.0.1:8080

WARNINGが出ていますが、後ほど解消するとして、先に実際にローカルホスト http://127.0.0.1:8080 にアクセスして結果を確認します。

It worksという文字が表示されているのを確認出来ました!

VaporプロジェクトのSources > App > routes.swiftに記述されているコードを確認すると、ルートパスがない場合でGETを実行した場合はIt works!と表示されるようになっております。

import Vapor

func routes(_ app: Application) throws {
    app.get { req async in
        "It works!"
    }

    app.get("hello") { req async -> String in
        "Hello, world!"
    }
}

パスをhelloにした場合は、Hello, world!という実行結果を得ることが出来るのでhttp://127.0.0.1:8080/helloにアクセスしてみましょう。

Hello, world!が確認出来ました!

警告を消す

デバッグコンソールに警告があったので解消しておきます。

[ WARNING ] No custom working directory set for this scheme, using /Users/your-user-name/Library/Developer/Xcode/DerivedData/hello-world-fhdbhamkzhbtycannplsuwagehin/Build/Products/Debug

このスキームではカスタムワーキングディレクトリが設定されていませんという内容なので設定してあげます。

Product > Scheme > Edit Schemes...を選択します。

表示された画面上で Run > Options を選択すると、ワーキングディレクトリが設定されていないのが確認出来ます。

Use Custom Working Directoryにチェックを入れて、任意のディレクトリを設定します。今回はVaporプロジェクトのディレクトリを設定しました。

これで警告は消えるようになりました。

JSONを返す

せっかくなので、ただの文字列だけではなく、JSONを返すところまで試してみたいと思います。

今回は、緯度経度と近くのランドマークの値を持つ構造体を作成しました。

struct Location {
    let latitude: Double
    let longitude: Double
    let nearLandmark: String
}

また、今回はapi/locationというルートにしたかった為、下記にようなルートを設定しました。ネストを深くしたい場合は、その分パスを追加するだけです。

// http://127.0.0.1:8080/api/location
app.get("api", "location") { req async -> [Location] in
    let location = Location(latitude: 34.39781747719756,
                            longitude: 132.47562536097118,
                            nearLandmark: "広島駅")
    return [location]
}

しかし、このままだとVaporフレームワークが用意しているContent型にLocationが準拠していない為、エラーが発生します。

エラーの指示通り、LocationContent型に準拠させるとエラーが消えます。

struct Location: Content {
    let latitude: Double
    let longitude: Double
    let nearLandmark: String
}

ランを実行して、http://127.0.0.1:8080/api/locationで結果を見てみましょう。

今回の目標だったJSONの値を確認出来ました!

おわりに

Swiftでサーバーサイドを実装する初歩的な内容を自分で試してみました。Swiftでサーバーサイドも書けるなんてワクワクしますね。これは充実した正月休みを送れそうです。

今回はGETまでだったので、POSTしたりDELETEしたり色々と試していきたいと思います。

参考