acb's technical journal

Posts matching tags 'html'

2016/2/25

swiftemplate: Compile-time HTML templates in Swift

Continuing my experiments with the potential of Swift for server-side web applications, I have recently turned my attention to the question of HTML templates. In any HTML-based web app, it is useful to have a way of programmatically generating HTML with data interpolated, which typically is done through templates; HTML documents with special markup. When a page is requested, the template is processed, with the data being inserted into it, generating a HTML document which is sent back to the browser.

There are two ways a templating system may be implemented in a web application framework. On one hand, the framework itself may embed the template engine; the web app in this case would be deployed with a set of templates, which it would parse into an internal representation; then, whenever the app receives a request, the engine uses the template to generate a HTML page. (This is the approach used by many web frameworks written in dynamic languages such as Python and Ruby.) The other alternative is to do the template processing at build time; a tool compiles the template into source code in the language the web application is written in; it is then included in the app, and called as any other piece of code would be. The advantages of this approach are more compact and efficient compiled applications. It is particularly suited to compiled languages like Swift, which already have a build process.

It is with this in mind that, following on from the Malimbe framework, I have developed swiftemplate, a familiar-looking HTML-oriented templating system that generates Swift code.

The swiftemplate syntax

The syntax of swiftemplate will be familiar to people who have used other web application frameworks. A template file consists of content (typically HTML) interspersed with directives, expressions and inline code blocks. Directives begin with %% and occupy their own lines; they include the familiar for and if/else (which are augmented with closing directives), as well as a template directive which defines a template and its arguments. Inline expressions are enclosed in <%= and %>; they must fit on one line, but may contain characters not permissible in Swift's \() interpolation syntax. Code blocks take up multiple lines, and are enclosed in <% and %> brackets (each of which must be on its own line). An example template (for displaying a page about a user of a web application) might look like like:

%% template showUser(user: User)
<%
let posts = user.posts.prefix(5)
%>
<h1>User \(user.name)</h1>
%% if posts.isEmpty
<p>This user has no posts</p>
%% else
<ul>
%% for post in posts
  <li><%= post.title %></li>
%% endfor
</ul>
%% endif
%% endtemplate

The template compilation process

Swiftemplate templates are compiled to Swift code with the swiftemplate command. One or more template files (each potentially containing one or more templates) may be compiled at a time into a Swift source file; one possible invocation could be to compile all the templates into one source filem prior to compilation of the Swift code, like so:

swiftemplate -o Sources/templates.swift Templates/*.swiftemplate

In the emitted source file, each template is converted to a Swift function, having the same name and arguments as in its %% template directive and returning a String; for example, the user page template above would produce Swift code that (if indented) looked something like:

func showUser(user: User) -> String {
  var _ℜ=[String]()
  let posts = user.posts.prefix(5)
  _ℜ.append("<h1>User \(user.name)</h1>")
  if posts.isEmpty {
    _ℜ.append("<p>This user has no posts</p>")
  } else {
    _ℜ.append("<ul>")
    for post in posts {
        _ℜ.append("<li>")
        _ℜ.append(String(post.title))
        _ℜ.append("</li>")
    }
    _ℜ.append("</ul>")
  }
  return _ℜ.joinWithSeparator(" ")
}

(The variable containing the return result is given the name ‘_ℜ’, which stands for “result” and is also unlikely to be used by the template. Needless to say, don't name your templates or template arguments this.)

Composing templates

Given that swiftemplate input files may contain multiple templates, and that templates, in their compiled form, are just Swift functions returning Strings, it is possible to define templates in a modular fashion. Below is an example, also from the user example, which displays a set of contact details using another template as a macro:

%% template contactDetail(name: String, value: String) 
<dt class="detailName">\(name)</dt> 
<dd class="detailValue">\(value)</dd>
%% endtemplate

%% template showUser(user: User)
<h1>User \(user.name)</h1>
<p>\(user.description)</p>
<h2>Contact details</h2<
<dl>
<%= contactDetail("Phone", user.phone) %>
<%= contactDetail("Email", user.email) %>
<%= contactDetail("Twitter", user.twitter) %>
%% // ... and so on
</dl>
%% endtemplate

The state of Swiftemplate

This is the first version of Swiftemplate, and it may well have bugs. Having said that, there are extensive unit tests (though, at the moment, these only work in Xcode; once testing in the Swift build process on Linux matures, they will work there as well).

Swiftemplate is a lightweight template processor; it parses the parts it understand and passes other parts (such as the expressions following an %% if or a %% for) through to Swift without attempting to enforce correct syntax. Which means that if those parts are nonsense, swiftemplate will not warn you, but you will get a Swift compiler error in the machine-generated .swift file containing your processed templates. Be warned!

Swiftemplate runs under OSX (with Xcode) or Linux (with the command-line build tools, and the Foundation module installed), and does not depend on Malimbe or any other code; it is available on GitHub and is distributed under the Apache Licence (i.e., it may be used freely, as long as acknowledgement is provided).

html swift swiftemplate web 0

This will be the comment popup.
Post a reply
Display name:

Your comment:


Please enter the text in the image above here: