Swift, it’s not just for mobile anymore…
What is Vapor? 💧
Vapor is a non-blocking, event-driven server architecture written in Swift and built on top of Apple’s SwiftNIO framework. It’s easy to get started with, has a large and growing community, and is built for performance and ease of use. It’s pretty sweet.
Vapor isn’t alone among the new crop of server-side Swift frameworks. There’s also IBM’s Kitura, Perfect and Zewo. Each of these has its pros and cons and I hesitate to say that there is a clear front runner in the space currently. I may look more at those other frameworks in the future, but for now I’ll focus on Vapor.
Getting Set up
These days ease of use can be a major determining factor when selecting a technology for a project. The Vapor team seems to be cognizant of this as getting setup is a breeze. They’ve even started Vapor University as a repository of curated Vapor tutorials.
My setup instructions are based on macOS, but you can follow along on Ubuntu too!
- Make sure Xcode is up to date. You can do this in the App Store.
- Check your swift version (you need at least 4.1.x)
$ swift — version
- Install brew if you have not already
$ /usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- Install vapor using brew
$ brew tap vapor/homebrew-tap
$ brew update
$ brew install vapor
- Check your vapor install
$ eval “$(curl -sL check.vapor.sh)”
- Create a new directory for your project. I’ve named mine smock.
$ mkdir smock
- Move into the new directory and create a new vapor project. I’ve passed the
—-apiflag here since we’re just concerned about building a backend server for this tutorial.
$ cd smock
$ vapor new smock --api
What is a Mock Server?
Before explaining what a mock server is, we’ll start with what a mock is and why you might use one. I’ll also avoid delving into the differences between a mock, a stub, and a fake. Just know that they’re all almost the same. Although there have been numerous articles written on the subject (for example) a general consensus among professionals is still hard to reach.
Generally, a mock, and to some extent a stub/fake is just some part of your code you need to substitute for testing purposes. The distinction I like is that a mock in and of itself does not have a predefined behavior.
The mock server I’ll share here is meant to act as a stand-in for any HTTP or HTTPS operations for your application under test.
I think having a server like this works well at the component or integration level of testing where you may have one or more different services that your code depends on which are not practical to make requests to while testing. While using a stub could achieve similar results, the use of a mock server will allow you to fully exercise any HTTP client code you may be using.
Conveniently, when you create a new vapor API server as we did earlier, vapor creates a simple hello, world and todo app within your new application. While we’ll end up deleting most of this code, it is helpful to see how a simple MVC is structured using vapor. Since this example application also goes through the trouble of bootstrapping a SQLite database we’ll go ahead and use that to store our mock responses. Under normal circumstances I would just recommend storing these as a dictionary in memory, but since the database is already wired up we may as well use it.
The MockResponse will be the primary object type we’re dealing with in our mock server. This class will expand on the SQLiteModel class which will allow us to use the convenient built-in methods for storage and retrieval of records.
MockResponse will include several properties including a unique ID, associated route, http method, response code, custom headers, and finally a payload.
Because this is meant to be a generic mock server it should be able to support a variety of different payload formats. Swift uses a protocol named Codable for encoding/decoding JSON. Unfortunately, Codable does not support dynamic dictionary structures, so I turned to a library named Codability which provides a class named AnyCodable that will allows for arbitrary dictionary structures to conform to the Codable protocol without having to be defined prior.
The MockController will act as the controller in our application. For simplicity it lacks the ability to update existing mock records and only handles record creation, deletion, listing, and most importantly retrieval and response formatting.
routes.swift handles routing within the vapor app and extends the Vapor Router class.
router.swift includes static routes for mock creation, listing, and deletion, as well as dynamic routes to support any mock endpoints you may end up adding. In order to fully support any url path I needed to rely on PathComponent.catchall, or just “all” as it can be accessed. This is a bit of a break from standard vapor routing and is really intended to serve as a generic request handler, which means some of the normal routing support paradigms aren’t available, like parameter handling.
I would NOT recommend using the all parameter for any kind of production service. It’s just opening yourself up to potential usability and security issues.
I ended up writing a new class to contain my utilities methods so I wouldn’t need to clutter my model or controller classes. These ResponseMapper functions are generally used for preparing the mock response by deciphering the AnyCodable payload attribute on the MockResponse object and using those details to produce the correct response body and headers.
Running the Server
You can build and run your vapor app from the command line by running.
$ vapor build && vapor run
You can also open the application in XCode and run there.
$ vapor xcode
Getting started with Vapor 3 was really a breeze. While I haven’t checked out its competitors yet, if your goal is to build a simple MVC application I would give it a shot.
The only issues I encountered during this project were finding accurate documentation (some things have changed since Vapor 2 and the documentation does not seem quite complete) and learning more about Swift’s Codable protocol and using Codability to conform to it.
Overall I would recommend Vapor and look forward to playing with it more in the future.
You can find my full code here.