Edited at

Zewoを用いてサーバサイドでSwiftを利用する

More than 3 years have passed since last update.


tl;dr

ZewoというSwiftをサーバサイドで使用するクロスプラットフォームなフレームワーク(プロジェクト?)の紹介をし、それを使用して簡単なウェブアプリケーションを実際に作ってみます。


Zewoとは

開発の背景は自分は把握していないのですが、2ヶ月ほど前からZewoというOrganizationがSwiftをサーバサイドで使用するためのモジュール群(と自分は認識)を以下で公開し始めています。

https://github.com/Zewo

公開しているものは例えば

など。まだ開発中ということもあって変更点も多く安定して使うのは難しそうですが、簡単なWebアプリケーションを作るためのコンポーネントを一通り提供してくれています。もちろんUbuntu上で動作します。

これらの各コンポーネントを組み合わせた実際のアプリケーション例もこちらで公開しています。


Zewo/Epochを利用したウェブアプリケーションの例

前述のように公式のサンプルアプリケーションがあるため、正直それを見れば十分です。

WebSocketやOpenSSLのヘルパーモジュールの使用例なども混ざっており少し複雑なため、もう少しシンプルな例で試してみました。


環境と事前準備

実行環境は以下です。


  • Ubuntu 14.04 (Vagrant使用)

  • Swift 2.2 (swift-2.2-SNAPSHOT-2015-12-10-a-ubuntu14.04)

  • PostgreSQL 9.3

また、前準備として同作者達が提供しているlibvenice, uri_parser, http_parserが必要になるため、以下で手順に従ってインストールしておく必要があります。


簡単なアプリケーション

main.swiftとして以下を用意します。

Epoch自体にはルーティングの機能はないため、Routerを併用、またMustacheを使用したレスポンスを返すためのモジュールであるSideburnsも使用します。

(なお、以下Ubuntu上で走らせているためimport Glibcが先頭に必要になりますが省略します。)


main.swift

import CHTTPParser

import CLibvenice
import Epoch
import HTTP
import Router
import Sideburns

let router = Router { route in
route.get("/") { request in
let data = ["name":"Taylor"]
return try Response(status: .OK, templatePath: "./views/top.mustache", templateData: data)
}
}

Server(port: 3001, responder: router).start()


/にアクセスがあった際にTaylorというnameをテンプレートエンジンに渡して描画するだけのコードです。

Zewoの各モジュールはSwift Package Managerに対応しているため、Package.swiftに以下のように依存パッケージを記載しておきます。

Adventがアプリケーション名です。


Package.swift

import PackageDescription

let package = Package(
name: "Advent",
dependencies: [
.Package(url: "https://github.com/Zewo/Epoch.git", majorVersion: 0, minor: 1),
.Package(url: "https://github.com/Zewo/Middleware.git", majorVersion: 0, minor: 1),
.Package(url: "https://github.com/Zewo/Sideburns.git", majorVersion: 0, minor: 1)
]
)


views/top.mustacheには以下を記述します。


views/top.mustache

<!DOCTYPE html>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample</title>
</head>
<body>
Hello {{name}}!
</body>
</html>

これらを以下のようなディレクトリ構成で配置します。

$ pwd

/home/vagrant/Advent
$ tree
...
|-- Package.swift
|-- views
| `-- top.mustache
`-- Sources
`-- main.swift

ビルドして実行し、curlをたたくと正しくレスポンスが返ってくることが確認できます。

$ pwd

/home/vagrant/Advent
$ swift build && .build/debug/Advent
$ curl localhost:3001
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample</title>
</head>
<body>
Hello Taylor!
</body>
</html>


Zewo/PostgreSQLと併用

WebアプリケーションとしてはDBに接続できないと面白みがない(ということにさせて下さいすみません)ので、前述のZewo/PostgreSQLを併用し、PostgreSQLから取得したレコードをレスポンスに入れてみます。


事前準備

libpq-devを先にインストールしておく必要があります。(Ubuntuの場合)

$ sudo apt-get install libpq-dev

PostgreSQL自体のセットアップは省きますが、testデータベース上で以下のクエリを流しレコードを準備してあります。

CREATE TABLE IF NOT EXISTS profiles (

user_id int NOT NULL PRIMARY KEY,
first_name varchar(64) NOT NULL,
last_name varchar(64) NOT NULL,
birthday date NOT NULL,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO profiles VALUES
(1,'Taro','Tanaka','2001-08-18','2015-09-16 17:45:57'),
(2,'Jiro','Tanak','2011-06-13','2015-09-16 17:45:57'),
(3,'Saburo','Tanaka','2010-01-23','2015-09-16 17:45:57'),
(4,'Taro','Yamada','2008-12-03','2015-09-16 17:45:57'),
(5,'Tom','Something','1991-12-31','2015-09-16 17:45:57');


PostgreSQLと接続したアプリケーション

先ほどのmain.swiftを少し変更し、/users/:idで指定されたIDのユーザのプロフィールを取ってくるようにしてみます。

Zewo/PostgreSQLは基本は単なるアダプタなので、クエリは直で書きます。

ついでに少しだけ例外処理を追加してみます。


main.swift

import CHTTPParser

import CLibvenice
import Epoch
import HTTP
import Router
import Sideburns
import PostgreSQL

let db = Connection("postgresql://root@localhost/test")
try db.open()

let router = Router { route in
route.get("/users/:id") { request in
do {
guard let userId = request.parameters["id"] else {
return Response(status: .NotFound)
}
let result = try db.execute("SELECT * FROM profiles where user_id = $1", parameters: userId)
guard let user = result.first else {
return Response(status: .InternalServerError)
}

let fullName = user["first_name"]!.string! + " " + user["last_name"]!.string!
let data = [
"name": fullName,
"birthday": user["birthday"]!.string!
]
return try Response(status: .OK, templatePath: "views/user.mustache", templateData: data)
} catch let error {
print(error)
return Response(status: .InternalServerError)
}
}
}

Server(port: 3001, responder: router).start()


Package.swiftにはZewo/PostgreSQLの記述を加え、以下の状態に。


Package.swift

import PackageDescription

let package = Package(
name: "Advent",
dependencies: [
.Package(url: "https://github.com/Zewo/Epoch.git", majorVersion: 0, minor: 1),
.Package(url: "https://github.com/Zewo/Middleware.git", majorVersion: 0, minor: 1),
.Package(url: "https://github.com/Zewo/Sideburns.git", majorVersion: 0, minor: 1),
.Package(url: "https://github.com/Zewo/PostgreSQL.git", majorVersion: 0, minor: 1)
]
)


テンプレートファイルはviews/user.mustacheとして用意しておきます。


views/user.mustache

<!DOCTYPE html>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample</title>
</head>
<body>
{{name}}
誕生日: {{birthday}}
</body>
</html>

この状態で先ほどと同じコマンドでビルド・実行しcurlをたたくと

$ curl localhost:3001/users/1

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample</title>
</head>
<body>
Taro Tanaka
誕生日: 2001-08-18
</body>
</html>

$ curl localhost:3001/users/2
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample</title>
</head>
<body>
Jiro Tanak
誕生日: 2011-06-13
</body>
</html>

大分シンプルですが、期待どおりの結果は得られていそうです。

これでPostgreSQLと接続し、Mustacheを使用してレンダするアプリケーションがSwiftで書けました。


まとめ

まだ開発が始まったばかりなので当たり前ですが、ドキュメントも殆どなく不安定ので中身を読みながら作ることになる(更に言えばUbuntu上で開発してるとSwift自体のbugもときたま踏む。。)ため特にオススメはしませんが、簡単なWebアプリケーションであれば上記のように作れることは確認できました。

Swiftをサーバサイドで扱うプロジェクトは他にも複数ありますが、個人的にはZewoが中身が読みやすくSwiftらしく、各コンポーネントも分離されていて良いなと思っています。

公式のSlackを眺めていると開発スピードがかなり速いようで見ていると面白いです。

ところで、今回中身にはほぼ触れていませんが、前述の通りEpochのベースになっているVeniceはlibmillが基になったlibveniceを使用していて、CSPの仕組みを基に動作しているようです。

Swift 3でconcurrencyが言語レベルではサポートされないという話もありますし、もう少しZewo全体の開発が落ち着いてきたらパフォーマンスも見てみたいなと個人的に思っています。