Welcome to the Bleeding Edge: Web Development in Swift

Ten days ago, I set out to write an app using Vapor, a web framework exponentially growing in popularity. With the help of the amazing Vapor community, and a number of educational videos, I was able to learn the foundations of Swift and Vapor. That said, I also learned the cost of developing with something so new.

This article will serve as an introduction to creating a Vapor project and a retrospective into the cost of developing with bleeding edge software. I’ll be discussing it from the viewpoint of a Ruby on Rails developer, but most of the takeaways should remain agnostic.

What is Swift?

Released in 2014, Swift was created as a solution to the antiquated Objective C language that powers the Apple platform. It’s syntax and language style follow that of more modern languages like Ruby or Python, but under the hood it’s an inherently compiled language like its older brother Objective C, making it blazingly fast.

Within the year the interest in Swift skyrocketed, reaching the Top 3 of Most Loved Languages on Stack Overflow in 2015 and in 2016. It began to dominate all mobile development, replacing Objective C as the primary language. Overall, it became a smash hit.

So of course, people wanted to use it for Web Development.

Meet Vapor

Thankfully, Apple heard the pleas to make web development happen, and open sourced the entirety of the language in December of 2015. This allowed a number of open source projects to spring up that leveraged Swift to build web frameworks, but running on a traditional Linux server node.

Currently at the forefront of this movement is a project called Vapor. Created by Tanner Nelson and Logan Wright, the project was launched in January of 2016 and just reached 1.0 as of September 15th, 2016(a mere three days prior to the writing of this post). Vapor now has over 5,800 stars on GitHub, is currently the most used dependency of all Swift web projects, and is listed second of all Swift projects on GitHub.

While other solutions exist, Vapor appears to be the most promising going forward.

Getting Started

In order to get started using Vapor and Swift, we will need to install a number of items. I will be writing this from the perspective of a Mac user, so as long as you have a relatively recent version of Mac OS X (I’m using El Capitan, but I’d assume Yosemite or something similar would suffice), you should be fine.

  • Install XCode 8. You can do this by opening the App Store, searching for XCode, and then hitting the button that ‘Get’ or ‘Install’ depending on if you’ve ever installed it before on a previous device. This process will take about 15 minutes on a decent internet connection. Go ahead and make some coffee, and then return when its finished.
  • Check it installed correctly by running the check tool.
curl -sL check.vapor.sh | bash
  • If it ran correctly, great. If not, verify you installed XCode 8 and check the version information for the Command Line Tools. The Vapor Documentation provides more information on that here.
  • Next, install the Vapor Toolbox. This allows us to easily create new projects and much more. Run the installer.
curl -sL toolbox.vapor.sh | bash
  • Finally verify it installed correctly by running the help command in the Toolbox.
vapor --help

Thats it for installation. Let’s move on to creating a new project!

Hello, World!

Creating a new project in Vapor is very similar to creating on in Rails, provided you have the Toolkit installed.

vapor new <App Name>

Easy, right? This will generate all the files necessary for your Vapor application.

At this point though, its important to mention that using a text editor like Atom, Sublime Text, or VS Code may not be the best solution. Unlike a language like Ruby, Swift apps can fail to build just due to a poor variable declaration. Put simply, it’s incredibly picky. To avoid dealing with that, I would recommend using the XCode IDE itself. Thankfully we can use the Toolbox to generate a new XCode project.

vapor xcode -y

This will create the XCode project and open it immediately.

Below I have outlined a brief description of the file structure of a Vapor project.

-VaporApp (Root)
-- Configs (XCode project settings)
-- Sources (Our code, including models, controllers, and routes)
-- Config (Vapor preferences)
-- Localization (Specific localization data for different languages)
-- Public (Images, CSS, JS, etc. )
-- Resources (All of our Views)
-- Dependencies (The relevant codebases for project dependencies)
-- Projects (The compiled version of dependencies)

There are also a number of files in the root directory. These are used for CI services, managing Swift versions, and other high-level tasks. For now, let’s leave it as default, and run our server.

There are two ways to do this, either by pressing the big play button in XCode, or via command line. Currently command line builds can result in full recompiles, rather than just changes, so I recommend instead using the play button in XCode.

Note, it may be necessary to tell XCode to run the app after it’s built. The version of Vapor I used did not have this by default. If that is the case, go to Product->Scheme->Edit Scheme or press command+< . Then select the Run tab on the left, click on the Executable dropdown, and select App.

After hitting play, the project will build, a command line will appear at the bottom showing server output, and the play button will dim while the stop button will become active. Pressing that will kill the server.

Finally, visit your new app by going to http://localhost:8080. You should see something similar to the below image.

Congratulations. You have now built a basic Vapor application!

Breaking It Down

Now that we have a running Vapor app, let’s take a look at what makes this possible. In the default example, almost all of this logic is handled in Main.swift, which can be found under Sources>App. We’ll split this up into the primary purpose of Main, where routing comes in, and then how we are displaying data to the user through Leaf views.

Main.Swift

Starting from the top, we are doing three major steps. First, we import the Vapor module, and then create a Droplet. The Droplet serves as the primary object for the app, and through it we register all routing, middleware (for things we want to happen on every request), and secondary services like database software.

import Vapor

let drop = Droplet()

Next, we create a route. As stated above, its a method directly called on our Droplet. In this instance, we do not provide a path, indicating that is for the root path (“/”). We also dictate a return value, which is a specific view and a variable passed to it called message, which uses the built in localization to pick the English entry for “welcome.”

drop.get { req in
  let lang = req.headers["Accept-Language"]?.string ?? "en"
  return try drop.view.make("welcome", [
  "message": Node.string(drop.localization[lang, "welcome", "title"])
  ])
}

Finally, we go ahead and run the droplet to serve the application.

drop.run()

Bringing in Leaf Views

In Main, we referenced the concept of views. Similar to Rails, Vapor uses view templates to create dynamic HTML pages. While Vapor supports numerous template engines, the default included is Leaf, which is a custom template system built for Vapor.

For the test app, two views are used: base.leaf and welcome.leaf. Base provides the default layout and then utilizes an import to leverage the Welcome and Head partials.

<!DOCTYPE html>
<html>
  <head>
    #import("head")
  </head>
  <body>
    #import("body")
  </body>
</html>

These partials both live in welcome.leaf, and are exported using explicit export statements, dictating the name of the partial.

#extend("base")
#export("head") {
  <title>My App</title>
  <link href="https://fonts.googleapis.com/css?  family=Quicksand:400,700,300" rel="stylesheet">
  <link rel="stylesheet" href="/styles/app.css">
}
#export("body") {
  <div class="message">
    <img   src="https://pbs.twimg.com/profile_images/760551690902900737/9vosg_bf.jpg">
    <h1>#(message)</h1>
  </div>
}

Adding to the Foundation

Thankfully, the above example is fairly simple. We merely utilize Main.swift and a couple of views to build a single page. Where Vapor really begins to shine as a full framework though is when you implement a full MVC design pattern. Just like Rails, Vapor supports:

  • Controllers
  • Models
  • Helpers (in the form of generic Swift objects)
  • Services (called Middleware)

Utilizing these, one can build a fairly complex app in Vapor with the same level of depth as a typical Rails app, but with far more speed and performance.

The Result

Initially, I had intended to include a section on implementing the above, eventually linking to a GitHub repository that implemented full CRUD, authentication, and authorization utilizing Vapor MVC.

Unfortunately, this is still ongoing.

The largest challenge to using Vapor is not the language, nor the framework itself. Instead, the relative age of Vapor is it’s biggest drawback. When compared to more established frameworks in other languages, things like documentation, support, and plain code samples just aren’t as abundant. At the end of the day what will make or break Vapor will be the adoption it sees and time given to mature. While the status of the aforementioned issues are constantly changing, the same volatility also harms the user, meaning that even hours after writing this article, new code changes could be pushed that possibly render the above tutorial no longer relevant. With the release of 1.0, a large number of database providers are no longer supported and official documentation has already gone out of date.

Of course I have no doubt these issues will be rectified, and may already be by the time this article is published, but it still presents an obstacle when deciding to use Vapor as a platform.

The Cost of Bleeding Edge

Incredibly new software comes at a price. The new features it provides and the performance it improves are without a doubt amazing, but honestly tend to result in a hit in productivity to the developer.

In a period of ten days, I was able to build a generic CRUD app that I would typically be able to do in twenty minutes, though I do admit not all time during those ten days were dedicated to Vapor. Regardless, towards the end of that period, all of my work became outdated the moment 1.0 released and the postgreSQL provider went out of date, rendering my models useless. Furthermore, authorization and authentication, two incredibly important features, felt almost impossible to implement for all but the most seasoned of users.

At the end of the day I did learn a valuable lesson. Software is constantly changing, regardless of its age. Typically we as developers can get away with relatively passive learning after an initial ramp up, but this was not the case with Vapor. When your tools are changing on a daily basis your resources become all the more important. Thankfully, Vapor has that in spades. The Slack channel is constantly active, commits and Pull Requests are abundant and well documented, and overall, its just a very pleasant team.

Would I recommend Vapor to a friend? Would I use it in production? Honestly, this depends on the comfort level and experience of the user, but I think my first question would be, “Can you afford to spend ten times the amount of time you would usually to learn something new and exciting?” If that’s the case, welcome to Server Side Swift.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.