よりひろい フロントエンド
Author : Kazuhiro Hara
Author : Kazuhiro Hara
Wed Dec 11 2024

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!"
            }
        }
    }
}

テンプレートは断片をコンポーネントとして入れ子にしていくことも出来る。コンポーネントについては次回以降書いていくつもり。

ElementarySwiftVaporXcode

Share

About site

「よりひろいフロントエンド」はじめました

いろいろやっている自分を一言で表す言葉として「よりひろいフロントエンド」を思いつきました。このサイトではこの言葉を中心に AI、XR、UI デザイン、ウェブ、バックエンドやその周辺のことを興味の赴くまま広くディスカバリーしていきます

About Me

カンソクインダストリーズのロゴ

「よりひろいフロントエンド」運営元 カンソクインダストリーズ では、フロントエンドを中心によろずご相談お受けいたします。お気軽にお問い合わせください。