Blogging in Swift (Part 3) (aka Vapor time)

https://daily.bandcamp.com/features/vaporwave-and-hip-hop

Introduction

This post follows where my server-side Swift Kitura adventures left off.

Some sad news came about when it was announced that Kitura was winding down, and I realized that, since I built my blog using Kitura, I should probably find an alternative with a more stable future.

I peeked around and Vapor caught my eye.

Vapor

Quoting their GitHub:

"Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project."

It's one of the most popular server-side Swift frameworks nowadays, powered by Swift NIO, and powers some pretty cool apps (like Scribble: an app that let's you share whiteboard space remotely!).

It even has some pretty nice integrations with things like SQLite and has some pretty nice ORM stuff with their Fluent library. For my blog it's pretty basic, and doesn't really use any of this though.

Fixing Routes

The first thing I had to do was setup a new vapor app using their guide.

Note: As of writing this, Vapor 4 is in the RC stages so it may very well be available by the time you're reading this.

The rest of it was relatively painless and just required me to restructure some of my routing.

What once looked like this in Kitura:

app.router.get("/") { request, response, next in
  guard let recentPosts = getAllPosts(atPath: blogFileServer.absoluteRootPath, limit: 6) else { return }

  do {
    try response.render("blog-home.stencil", context: [ "posts": recentPosts.map { $0.dictionaryRepresentation } ])
    response.status(.OK)
  } catch let error {
    response.send(json: ["Error": error.localizedDescription])
  }

  next()
}

Became this in Vapor:

router.get("/") { request -> Future<View> in
  guard let recentPosts = getAllPosts(atPath: rootBlogPath, limit: 6) else { throw Abort(.notFound, reason: "") }

  return try request.view().render("blog-home", ["posts": recentPosts.map { $0.dictionaryRepresentation }])
}

What's nice is the API changes slimmed things down to a significantly more simple way of defining the root page for my blog.

You'll notice that requests return Futures of Views, which will resolve at some point in the future (reminds me a lot of Hack!).

Also, this gets rid of the next() invocations littered about when using Kitura, which I never personally really understood that well.

Stencil to Leaf

The next thing I had to port over were my stencil definitions. Kitura uses Leaf, which is another way of defining HTML templates that Vapor can populate.

They were basically the same, except for a few syntactic changes, namely the use of #(something) instead of {{ something }}.

You can check out the documentation for Leaf here.

Kitura:

<!DOCTYPE html>
<html>
  <body>
    <ul>
      {% for post in posts %}
      <li>
        {{ post.title }}
      </li>
      {% endfor %}
    </ul>
  </body>
</html>

Vapor:

<!DOCTYPE html>
<html>
  <body>
    <ul>
      #for(post in posts) {
      <li>
        #(post.title)
      </li>
      }
    </ul>
  </body>
</html>

Conclusion

For my case, switching over was really simple and felt like a relatively painless API migration. What's kinda neat too is, at least in my case, Vapor is noticeably more stable when running my blog. It hasn't gone down once since porting over (which would happen every month or so with Kitura).

In addition, compile times seem to have been halved, although I'm not sure if this is just tangential to Swift improvements or not.

Vapor seems great to me! I'm sad to see Kitura go, but hopefully this one stays around!

Give it a go if you're interested!