VaporでLeafを使ったサンプルアプリケーションの実装


Leaf は Swift の影響を受けたテンプレートエンジニアで、動的な HTML を生成することができます。

Leaf is a powerful templating language with Swift-inspired syntax. You can use it to generate dynamic HTML pages for a front-end website or generate rich emails to send from an API.

Leaf Overview

Webモードでプロジェクトを作成するとすぐに Leaf を使うことができます。
Vapor の環境構築やプロジェクト作成はこちら↓
Swift製Webフレームワーク Vaporの紹介2019

プロジェクトが作成されたら、まずは routes.swift を確認してみましょう。

routes.swift
// "It works" page
router.get { req in
    return try req.view().render("welcome")
}

// Says hello
router.get("hello", String.parameter) { req -> Future<View> in
    return try req.view().render("hello", [
        "name": req.parameters.next(String.self)
    ])
}

2つのルーティングが設定されています。
1つは トップページです。/ にアクセスすると it works! の文言とロゴが表示されます。
もう1つは /hello/{String} です。 /hello の後ろに文字列のパラメーターを入力すると、このルーティングにマッチします。
具体的には http://localhost:8080/hello/Kabigon というアドレスにアクセスすると、 Hello, Kabigon!と表示されます。

次に base.leaf を見ていきます。
welcome.leafbase.leaf を読み込み、title と body を埋め込んでいます。
base.leaf hello.leaf でも使われるベースとなるファイルで、ほぼ HTML です。
とっても簡単ですね。

base.leaf
<!DOCTYPE html>
<html>
<head>
    <title>#get(title)</title>
    <link rel="stylesheet" href="/styles/app.css">
</head>
<body>
    #get(body)
</body>
</html>
welcome.leaf
#set("title") { It works }

#set("body") {
    <div class="welcome">
        <img src="/images/it-works.png">
    </div>
}

#embed("base")

今回は Vapor を使って 2chまとめのまとめ の技術ブログ版のようなサイトを作ります。
まずは技術ブログのタイトルを表示してみようと思います。クックパッドさんとクラスメソッドさんの技術ブログが特に大好きなので、まずはその2つのサイトのブログ名を表示してみます。

2chmm.leaf というファイルを作成します。blogs という文字列が入った配列を受け取り、blogs の要素数だけリストを表示します。

2chmm.leaf
#set("title") { 技術ブログまとめ }

#set("body") {
    <ul>
    #for(blog in blogs) {
        <li>#(blog)</li>
    }
    </ul>
}

#embed("base")

routes.swift2chmm.leaf に 文字列の入った blogs 配列を渡します。
これでブログ名を並べて表示することができました。

routes.swift
import Vapor

public func routes(_ router: Router) throws {
    // 2chmm page
    router.get { req -> Future<View> in
        return try req.view().render("2chmm", [
            "blogs": ["クックパッド開発者ブログ", "クラスメソッド発「やってみた」系技術メディア | Developers.IO"]
        ])
    }
}

ただ、文字列だけでは使いづらいので、自分で定義した Struct を leaf 側に渡してみようと思います。
まずは Blog という Struct を作成します。leaf に渡す Struct は Encodable プロトコルに準拠する必要があります。

Blog.swift
struct Blog: Encodable {
    let name: String
    let url: String
}
routes.swift
import Vapor

public func routes(_ router: Router) throws {
    // 2chmm page
    router.get { req -> Future<View> in
        return try req.view().render("2chmm", [
            "blogs": [
                Blog(name: "クックパッド開発者ブログ", url: "https://techlife.cookpad.com/"),
                Blog(name: "クラスメソッド発「やってみた」系技術メディア | Developers.IO", url: "https://dev.classmethod.jp/")
            ]
        ])
    }
}
2chmm.leaf
#set("title") { 技術ブログまとめ }

#set("body") {
    <ul>
    #for(blog in blogs) {
        <li>
          <a href="#(blog.url)">#(blog.name)</a>
        </li>
    }
    </ul>
}

#embed("base")

これで以下のような HTML が出力されます。

<!DOCTYPE html>
<html>
<head>
    <title> 技術ブログまとめ </title>
    <link rel="stylesheet" href="/styles/app.css">
</head>
<body>    
    <ul>
        <li>
          <a href="https://techlife.cookpad.com/">クックパッド開発者ブログ</a>
        </li>
        <li>
          <a href="https://dev.classmethod.jp/">クラスメソッド発「やってみた」系技術メディア | Developers.IO</a>
        </li>
    </ul>
</body>
</html>