Hello World (Again)

After a brief delay (a mere four years) it seemed like a good opportunity to bring this site up-to-date.

Welcome back

This site was originally an opportunity to experiment with technologies such as Swift, Vapor, Fluent, and Leaf. Now it is an opportunity to update the site and to explore what broke and changed in the interim.

Leaf changes

Embedding templates

Leaf templating syntax has changed, particularly with regard to the way in which embedded components are defined and used.

The old approach involved setting the component, and then specifying the template in which it was to be embedded.

#set("content") { <article> <h1>#(page.title)</h1\> <p>#(page.introduction)</p> #(page.body) #embed("discuss") </article> } #embed("base")

The new approach involves specifying the template to be extended, and then exporting the component. Note too the change from { } syntax.

#extend("base2"): #export("content"): <article> <h1>#(page.title)</h1> <p>#(page.introduction)</p> #(page.body) </article> } #endexport #endextend

Embedding raw HTML

The old method of #get became #raw which then became #unsafeHTML

Control flow

The control flow statements switching from using { } syntax.

#if(...) { ... } else { ... }


#if(...): ... #else: ... #endif

Vapor Changes

From Future to async/await

Vapor has changed considerably since v1, and one of the biggest changes from earlier versions has been the adoption of async/await, which has removed a lot of the complexity of using Future<>.

The old function that displayed a page:

func displayPage(_ request: Request) throws -> Future<View> { let slug = try request.parameters.next(String.self) return Page.query(on: request) .filter(\.slug == slug) .first() .unwrap(or: Abort(.notFound)) .flatMap { page -> Future<View> in return Page.query(on: request) .filter(\.created > page.created) .sort(\.created, .ascending) .first() .and(Page.query(on: request) .filter(\.created < page.created) .sort(\.created, .descending) .first()) .flatMap { nextPage, previousPage -> Future<View> in let context = PageContext(page: page, previousPage: previousPage, nextPage: nextPage, author: page.toAuthor.get(on: request), tags: try? page.toTags.query(on: request).all()) return try request.view().render(Templates.page.rawValue, context) } } }

has become a much cleaner async/await version:

func displayPage(request: Request) async throws -> View { guard let slug = request.parameters.get("slug"), let page = try await Page.query(on: request.db) .filter(\.$slug == slug) .first() else { throw Abort(.notFound) } let previousPage = try await Page.query(on: request.db) .filter(\.$created < page.created) .sort(\.$created, .descending) .first() let nextPage = try await Page.query(on: request.db) .filter(\.$created > page.created) .sort(\.$created, .ascending) .first() let context = PageContext(page: page, previousPage: previousPage, nextPage: nextPage) return try await request.view.render("page", context) }

Note that there are more optimizations that can be made in this function; this was a quick migration from Future<> to async/await.