Swiftpack.co - Package - MatsMoll/Safeleaf


Render Leaf templating files in a typesafe way. By combining the Leaf templating language, and the Swift programming language, you can catch bugs earlier in the process.

Config your Vapor project

in your config file, add the provider and sett the LeafViewConfig with the views you want to use.

public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    try services.register(LeafViewProvider())
    services.register { (container) -> LeafViewConfig in
        var config = try LeafViewConfig(container: container)
        try config.add(BaseView.self)
        try config.add(WelcomeView.self)
        try config.add(HelloView.self)
        return config

This will automatically generate your views and save the leaf files to disc. This will make it possible for you to edit the file, and "hot-refresh" the views when developing.

There will be service to convert HTML to the swift format, in order to make the process easier. But this is not completely done as of now

Render the views

You can render the views to the user by calling some helper functions. There are two options for two different type of views.

router.get { req in
    return try req      // Renders a LeafTemplate that take a context variable
        .render(template: DynamicWelcomeView.self, 
                with: "Hello")

router.get { req in
    return try req      // Renders a StaticLeafView that do not need any context
        .render(static: StaticWelcomeView.self)

So how does it work?

Lets create a simple static view that is only a HTML div tag with some attributes and some text inside.

final class SimpleView: StaticLeafView {

    static func buildLeaf() throws -> ViewRenderable {
        //  <div id='Test' class='some-class'>Hello</div>
            div(attributes: [.id("Test"), .class("some-class")], children: "Hello")

final class SimpleTemplate: LeafTemplate {

    var content: SimpleData!

    static func buildLeaf() throws -> LeafViewRenderable { 
        //  <div>
        //      <h1>#(content.title)</h1>
        //      <p>#(content.description)</p>
        //  </div>
        return try
                h1(children: variable(\.content.title)),
                p(children: variable(\.content.description))

By conforming to LeafBuildable, StaticLeafView or LeafTemplate, you get access to the helperfunctions that make it easy to create.

Whats the differance between these three?

  • StaticLeafView is a static view that needs no variables to render. Shown above in SimpleView
  • LeafTemplate is a view that needs some content to render. Shown above in SimpleTemplate. Here you can se the renderd leaf has two variables.
  • LeafBuildable a generic class that can build a leaf file

But what about embedding files?

This is done safely by checking if the variable names match in the embedded view and the view embedding it. Otherwise there will be an error thrown for you to handle.

final class BaseTemplate: LeafTemplate {

    static var contentPath: WritableKeyPath<BaseTemplate, SimpleData> = \.content

    var content: SimpleData!
    var views: BaseTemplateView!

    static func buildLeaf() throws -> LeafViewRenderable { 
        //  <html>
        //      <head>
        //          <title>#(content.title)</title>
        //          <meta name='description' content='#(content.description)'/>
        //          #get(views.extraHeader)
        //      </head>
        //      <body>#get(views.content)</body>
        //  </html>
        return try  
                    title(children: variable(\.content.title)),
                    meta(attributes: .name("description"), .content(variable(\.content.description))),

final class UsingBaseTemplate: LeafBuildable {

    let content: SimpleData     // Checked if this variable name is the same as BaseTemplate.content

    static func buildLeaf() throws -> ViewRenderable {
        //  #set("views.extraHeader") {
        //      <link href='some-url'/>
        //  }
        //  #set("views.content") {
        //      <h1>Some title</h1>
        //  }
        //  #embed("BaseTemplate")
        return try [
            set(for: \BaseTemplate.views.extraHeader) {
                link(href: "some-url")
            set(for: \BaseTemplate.views.content) {
                h1(children: "Some title")
            embed(template: BaseTemplate.self)

Swift Package Manager:

To add this framework, add the package manager below

.package(url: "https://github.com/MatsMoll/Safeleaf.git", .branch("master")),


Stars: 0
Help us keep the lights on

Used By

Total: 1