11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SwiftAdvent Calendar 2017

Day 13

Kitura さわってみた

Last updated at Posted at 2017-12-13

はじめに

最初は Windows 用の Swift コンパイラのビルド方法について書いてみようかと思ったのですが、残念ながら使い物になるコンパイラができませんでした。そこでお題をかえて Swift で動く Web アプリケーションフレームワークの Kitura をちょっといじってみたいと思います。今思えば Server Side Swift Advent Calendar 2017 でやるべきネタでした。@SatoTakeshiX さんごめんなさい...

Kitura

Swift で Web サービスをつくるフレームワークらしい。IBM 製。

インストール

Homebrew に Kitura のリポジトリを追加してインストールします。

$ brew tap ibm-swift/kitura
$ brew install kitura

Hello World

まず npm init みたくプロジェクトディレクトリを作ってから kitura init します。

$ mkdir HelloKitura
$ cd HelloKitura
$ kitura init

するとなにやら大量にファイルがインストール & ビルドされてプロジェクトディレクトリができあがります。IBM のクラウドサービスである Bluemix にデプロイするためっぽいファイルもありますね。

$ ls -a
.                             .swiftservergenerator-project README.md
..                            .yo-rc.json                   Sources
.bluemix                      Dockerfile                    Tests
.build                        Dockerfile-tools              chart
.cfignore                     HelloKitura.xcodeproj         cli-config.yml
.dockerignore                 LICENSE                       manifest.yml
.gitignore                    Package.resolved              spec.json
.swift-version                Package.swift

HelloKitura.xcodeproj を Xcode で開いて Source/Applicaiton/Application.swift の postInit() の末尾に GET に対するハンドラを書きます。

router.get("/") { request, response, next in
    response.send("Hello, World!")
    next()
}
スクリーンショット 2017-12-11 0.02.06.png

ターゲットが大量にありますが、 その中の HelloKitura を選んで実行します。
スクリーンショット 2017-12-11 0.01.19.png

すると localhost:8080 で HTTP サーバーが動くのでブラウザ http://localhost:8080/ をひらくと Hello, World! と表示されるはずです。簡単ですね!

Memo サービス

要素として ID とテキストだけをもつメモを GET, POST, PUT, DELETE 出来る簡単な Web サービスを作ってみましょう。

モデルの作成

まずはさっきと同じように KituraMemo ディレクトリを作り、その中で kitura init して Kitura のプロジェクトを作ってください。KituraMemo.xcodeproj を開き、Application.swift にメモを表す構造体を追加します。

public struct Memo: Codable {
    public var id: Int?
    public var text: String?
}

Codable に準拠していると Kitura が JSON へのシリアライゼーション/デシリアライゼーションを自動的にやってくれます。この Codable 対応が Kitura 2 の目玉のひとつだそうです。

POST

新しいメモを投稿できるようにするため POST メソッドを実装しましょう。まず、let couldEnv = CloudEnv() の下に次を追加してください。

private var memos = [Int: Memo]()
private var nextID = 0

memos は投稿されたメモを保持する辞書でキーはメモの ID です。nextID が次に投稿されるメモにつける ID で、新しいメモが投稿される度にインクリメントすることにします。次に POST のハンドラを Kitura に登録します。postInit() の最後に次を追加してください。

router.post("/memos") { (memo: Memo, respondWith: (Memo?, RequestError?) -> Void) in
     let id = self.nextID
     self.nextID += 1

     let new = Memo(id: id, text: memo.text)
     self.memos[id] = new

     respondWith(new, nil)
}

これで /memos に POST するとその JSON の内容がクロージャ引数の memo に格納されて呼び出されるので辞書に保存します。respoendWith(_:_:) を呼ぶとクライアントにレスポンスが返ります。

KituraMemo ターゲットを実行している状態で実際に curl で POST してみましょう。Swift エンジニアたるものパフォーマンスは常に気になります。ターミナルで飽くなき速度への想いをぶちまけましょう!

$ curl -s -X POST -H 'Content-Type:application/json' -d '{"text":"5000兆フロップス欲しい!"}' http://localhost:8080/memos | jq
{
  "id": 0,
  "text": "5000兆フロップス欲しい!"
}

jq は JSON 文字列を見やすく整形してくれるコマンドです。respondWith(_:_:) の第一引数で返したオブジェクトが返ってきています。

GET

POST できたものの本当にこの想いがサーバーに伝わっているかちょっと怪しいです。次はサーバーが保持しているメモを取得する GET メソッドを実装してみましょう。先程と同じように postInit() に GET に対するハンドラを追加します。

router.get("/memos") { (respondWith: ([Memo]?, RequestError?) -> Void) in
    respondWith(self.memos.values.map({ $0 }), nil)
}

このように Memo の配列を返すだけす。curl で叩いてみましょう。次のようになれば成功です。Yes, we need more power!

$ curl -s -X POST -H 'Content-Type:application/json' -d '{"text":"欲しい!"}' http://localhost:8080/memos | jq
{
  "id": 1,
  "text": "欲しい!"
}
$ curl -s -X POST -H 'Content-Type:application/json' -d '{"text":"欲しい欲しい!"}' http://localhost:8080/memos | jq
{
  "id": 2,
  "text": "欲しい欲しい!"
}
$ curl -s -X GET http://localhost:8080/memos | jq
[
  {
    "id": 2,
    "text": "欲しい欲しい!"
  },
  {
    "id": 0,
    "text": "5000兆フロップス欲しい!"
  },
  {
    "id": 1,
    "text": "欲しい!"
  }
]

全てのメモを取得するだけではなく、ID を指定して特定のメモだけを取得できるようにもしてみましょう。それにはもう一つ GET ハンドラを追加します。

router.get("/memos") { (id: Int, respondWith: (Memo?, RequestError?) -> Void) in
    if let memo = self.memos[id] {
        respondWith(memo, nil)
    } else {
        respondWith(nil, .notFound)
    }
}

http://localhost:8080/memos/1 というよに ID を指定してリクエストするとこちらのハンドラが呼ばれクロージャ引数の id に 1 が渡ってきます。ID に対応するメモが見つからない場合は第二引数に .notFound をつけて respondWith(_:_:) を呼び、クライアントに 404 を返します。他にも HTTP のエラーが一通り定義されています。

curl はこうなります。

$ curl -s -X GET http://localhost:8080/memos/0 | jq
{
  "id": 0,
  "text": "5000兆フロップス欲しい!"
}

欲しい!

PUT

既存のメモを更新するため PUT ハンドラを実装します。クロージャ引数に ID と新しいメモの内容が渡ってきます。

router.put("/memos") { (id: Int, memo: Memo, respondWith: (Memo?, RequestError?) -> Void) in
    if self.memos[id] != nil {
        let modified = Memo(id: id, text: memo.text)
        self.memos[id] = modified
        respondWith(modified, nil)
    } else {
        respondWith(nil, .notFound)
    }
}

curl はこうなります。

$ curl -s -X GET http://localhost:8080/memos | jq
[
  {
    "id": 2,
    "text": "欲しい欲しい!"
  },
  {
    "id": 0,
    "text": "5000兆フロップス欲しい!"
  },
  {
    "id": 1,
    "text": "欲しい!"
  }
]
$ curl -s -X PUT -H 'Content-Type:application/json' -d '{"text":"5000兆円欲しい!"}' http://localhost:8080/memos/0 | jq
{
  "id": 0,
  "text": "5000兆円欲しい!"
}
$ curl -s -X GET http://localhost:8080/memos | jq
[
  {
    "id": 2,
    "text": "欲しい欲しい!"
  },
  {
    "id": 0,
    "text": "5000兆円欲しい!"
  },
  {
    "id": 1,
    "text": "欲しい!"
  }
]

そうだ、俺は金が欲しかったんだ!!

DELETE

( ゚д゚)ハッ!

ハイパフォーマンスを追い求めていたはずなのにうっかりお金のため逮捕されるのは勘弁です。DELETE も実装して証拠隠滅を図りましょう...

router.delete("/memos") { (id: Int, respondWith: (RequestError?) -> Void) in
    if self.memos[id] != nil {
        self.memos.removeValue(forKey: id)
        respondWith(nil)
    } else {
        respondWith(.notFound)
    }
}

では実際に消してみましょう。

$ curl -s -X GET http://localhost:8080/memos | jq
[
  {
    "id": 2,
    "text": "欲しい欲しい!"
  },
  {
    "id": 0,
    "text": "5000兆円欲しい!"
  },
  {
    "id": 1,
    "text": "欲しい!"
  }
]
$ curl -X DELETE http://localhost:8080/memos/0
$ curl -s -X GET http://localhost:8080/memos | jq
[
  {
    "id": 2,
    "text": "欲しい欲しい!"
  },
  {
    "id": 1,
    "text": "欲しい!"
  }
]

消せました。
ヤッタネ...

出来上がりは GitHub に置いておきます。ビルドするには最初にプロジェクトディレクトリで swift build してください。

Docker で動かしてみる

そういえばプロジェクトディレクトリに Dockerfile があるのをきになりました。KituraMemo を Docker で動かしてみましょう。まずは Docker for Mac をインストールします。.dmg ファイルをダウンロードしてアプリケーションフォルダに入れて起動するだけです。メニューバーの Docker メニューに Docker is running と表示されたら準備完了です。
スクリーンショット 2017-12-13 22.17.35.png

Docker 用にビルド

KituraMemo を Docker で動かすには Docker 用、つまり Linux 用にビルドし直さないといけません。プロジェクトファイルにあった Dockerfile は Kitura アプリの実行用の Docker イメージを作るためのものでビルドは出来ないようです。そこでビルド用の Docker イメージをダウンロードします。

docker pull ibmcom/swift-ubuntu:4.0

次に Docker イメージ内にプロジェクトディレクトリをマウントしつつ、起動します。

docker run -i -t -v (KituraMemo プロジェクトディレクトリのフルパス):/root/KituraMemo ibmcom/swift-ubuntu:4.0 /bin/bash

成功するとプロンプトが root@176e6bf9af3d:~# というようになり、Docker イメージ内でシェルを実行している状態になります。そうしたらプロジェクトをビルドします。

# cd ~/KituraMemo
# swift build --build-path ./.build-ubuntu --configuration release

~/KituraMemo/.build-ubuntu/release/KituraMemo ができれば成功です。exit でビルド用の Docker イメージから抜けてください。

Docker 上で実行

もちろんビルドに使ったイメージexで上で実行することもできるのですが、せっかくなのでプロジェクトディレクトリ内の Dockerfile で作ったイメージ上で実行してみましょう。プロジェクトディレクトリで次のコマンドを実行します。(注: ビルド用の Docker イメージ内ではなく、Mac のシェル上での操作です。)

$ docker build -t kitura_memo .
Sending build context to Docker daemon  322.7MB
()
$ docker image ls
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
kitura_memo                   latest              052e942a5439        9 seconds ago       605MB
ibmcom/swift-ubuntu-runtime   4.0                 7a4cf16d6bdd        4 weeks ago         293MB
ibmcom/swift-ubuntu           4.0                 c17f1e0377a2        4 weeks ago         1.36GB

イメージができたら起動します。-p オプションで localhost の 8080 ポートを Docker イメージの 8080 に転送するようにしておきます。

$ docker run -d -p 8080:8080 kitura_memo:latest

この状態で curl で叩いてみてレスポンスが返ってくれば成功です!

$ curl -s -X POST -H 'Content-Type:application/json' -d '{"text":"5000兆円欲しい!"}' http://localhost:8080/memos | jq
{
  "text": "5000兆円欲しい!",
  "id": 0
}

まとめ

駆け足になりましたが、Kitura をつかって簡単な POST/PUT/GET/DELETE ができる Web サービスを作り、Docker 上で動かしてみました。やりたいと思いつつ時間切れになってしまったのですが、Bluemix で動かすのも試してみたいですね。

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?