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") {
#(page.title)
#(page.introduction)
#(page.body)
#embed("discuss")
}
#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"):
#(page.title)
#(page.introduction)
#(page.body)
}
#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 {
...
}
became:
#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 {
let slug = try request.parameters.next(String.self)
return Page.query(on: request)
.filter(\.slug == slug)
.first()
.unwrap(or: Abort(.notFound))
.flatMap { page -> Future 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 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.