Vapor のテンプレートに Elementary を使う
Vapor のテンプレートはフレームワークのデフォルトだと Leaf というテンプレートエンジンがある。これはプロジェクト作成時でも選択ができる公式にサポートされたテンプレートエンジンだ。今回はテンプレートエンジンに Elementary を使ってみるという話。
Elementary のリポジトリは以下。
また、Vapor で使うためのパッケージは別途リポジトリが存在する。
サーバーサイド Swift のテンプレートエンジンには大きく分けて2種類のタイプがあると思っている。一つは、昔ながらの変数埋め込み型のものだ。例えば Mustache なんかはその代表例だと思う。
Leaf もその流れをくむもので、Swift 言語の影響下にある記述スタイルであるものの、HTMLファイルがあり、そこに置き換えをするためのテンプレートタグを入れていくというスタイルだ。
もう一つの流れは SwiftUI 系の流れを汲むもの。基本的には Swift コードで、その中の一部が DSL 的な感じでタグの構造を定義していく。iOS や Mac アプリを作っている人にはお馴染みの方法だ。宣言的 UI とも呼ばれたりする。
このタイプのテンプレートは既にいろいろな種類があるが、Elementary は後発のライブラリだ。既にいろいろあるのに作成された理由が Readme に記載がある。
「Motivation and other packages」には以下のように書かれている。
Plot, HTMLKit, and Swim are all excellent packages for doing a similar thing.
My main motivation for Elementary was to create an experience like these (Swift Forums post for more context), but
- stay true to HTML tag names and conventions (including the choice of lowercase types)
- avoid allocating an intermedate structure and go straight to streaming HTML
- using generics to stay away from allocating a ton of lists of existential anys
- have a list of attributes go before the content block
- provide attribute fallthrough and merging
- zero dependencies on other packages
Plot、 HTMLKit、 Swim はいずれも後者のタイプのテンプレートライブラリだが、最近はリリースの勢いがあまりない。また、方言のようなものが各ライブラリにはそれぞれあり、合う合わないがありそうだ。モチベーションの一つとして HTML のタグ名や規約に沿うというものがあり、HTML を知っている人からするとすんなり使い始めやすい。
そんな Elementary だが、素晴らしいのは Vapor と Hummingbird いずれのフレームワークでも使えることだ。愛用している Vapor で早速導入してみよう。
今回は Hello World 的なものを出すだけのシンプルなプロジェクトを作る。
ライブラリの導入
まず初めに Package.swift
に依存情報を記載していく。2箇所 dependencies
に追加で記述をするほか、 platforms
のバージョン番号を書き換える必要がある。どうやら v14
以降でないと対応していないらしい。書き換えた Package.swift
は以下のようになる。
Package.swift
...
let package = Package(
...
platforms: [
// v14 に修正
.macOS(.v14)
],
dependencies: [
...
// elementary の依存設定を追加
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0")
],
targets: [
.executableTarget(
...
dependencies: [
...
// elementary の依存設定を追加
.product(name: "VaporElementary", package: "vapor-elementary")
],
...
),
...
],
...
)
...
テンプレートの記載
続いて /Sources/App/routes.swift
にテンプレートを記載し、ルーティング情報の書き換えをする。
テンプレートのレンダリングには HTMLResponse
を使う必要があり、これは VaporElementary
パッケージに含まれているため、インポート宣言を記述する。
今回はルーティング定義の下にそのまま ElementaryPage
というテンプレートを記述してしまっているが、これは単なる Swift コードであるため、別ファイルにしたりいろいろと自在に変更することができる。
ページ用のテンプレートは HTMLDocument
プロトコルを実装する必要がある。具体的には title
と head
と body
だ。階層構造でタグを記述していく感じは SwiftUI にかなり近いため、Swift を使う側としてはこちらのほうが自然に感じられる。HTMLの属性はちょっと書き方が独特なので例として h1
要素に id
と class
を指定してみた。
以下のような感じでテンプレートを書いていける。
import Elementary
import Vapor
import VaporElementary
func routes(_ app: Application) throws {
app.get { _ async in
HTMLResponse {
ElementaryPage()
}
}
}
struct ElementaryPage: HTMLDocument {
var title = "Elementary"
var head: some HTML {
meta(.charset(.utf8))
}
var body: some HTML {
main {
h1(.id("my-h1-id"), .class("my-h1-class")) {
"Hello Elementary!"
}
}
}
}
テンプレートは断片をコンポーネントとして入れ子にしていくことも出来る。コンポーネントについては次回以降書いていくつもり。