I recently gave a presentation to my local NSCoders group. Here’s that talk translated into a blog post.
Server Side Swift with Vapor
I recently worked on a backend server for a mobile app, a sales contest support app. It helps salespeople track their points in a sales contest. The server just vends REST endpoints, mostly JSON data. The backend has no interface intended for human interaction. I wrote this backend in Swift, building my server on the Vapor framework. Here’s what I learned working on this project.
About the Project
Our customer has a large sales staff. They are running an 11 week sales contest this fall, and wanted an app to support that contest. The app reports the individuals points and their standings in their region. The best performers in each region get cash prizes each week, and at the end of the contest, the best overall performer earns the grand prize. Additionally, the project came to us late, with very little time to work on the project. Our normal backend developers were too busy, so I, the company’s specialist in iOS development, stepped forward and offered to do it. I just insisted that I must be allowed to be creative in how I solved the problem.
We knew this is a short term project. We had to get it done quickly, and we knew it would only be useful for a few months. This allowed us to consider Swift on the server. The available frameworks are all new and changing rapidly, but we didn’t care about long term compatibility and maintainability of this project, because come January the contest will be over.
There are lots of choices for writing Swift on the server. Apple ported Swift to Linux, and ever since there’s be excitement about server side swift. There are many projects to build Swift based server frameworks. I considered the three that seemed most popular to me, Vapor, Perfect, and Kitura.
I didn’t have very much time to make my decision on which framework to use, so I just gave each a few hours to evaluate. I tried each framework’s basic tutorials, and looked over the documentation. They all seemed capable of meeting the needs of the project. It’s just that Vapor seemed to click with me faster than the other frameworks. It just made more sense to me. So I went with Vapor.
Documentation and Community
Vapor has some good documentation, it’s more extensive than the other frameworks. Yet I think it suffers a little from a typical problem I see with documentation. The author(s) don’t seem to realize where their knowledge leaves off and were the audience’s knowledge starts. There were a few points where they made assumptions that every backend developer should know, but their audience includes first time backend developers.
There is an online community for Vapor. There’s a Slack for Vapor, with many participants and lots of traffic. I’ve asked a few questions, and helped out a few others. Much of the public work on Vapor happens in GitHub. Plus, there are some questions and answers on Stack Overflow.
Unfortunately, Google associates the term “Vapor” to electronic cigarettes, which makes it harder to come up with good search terms. Fortunately, Taylor Swift doesn’t vape, so “Vapor Swift” works as a search term.
Deployment is the thing you do at the end, right? Wrong. I felt it was important to establish that I could deploy a Vapor project before I spent too much time building the project. It would have been disastrous if I found out too late that I couldn’t get the project on a server.
Secondly, Swift on Linux includes Foundation, but Foundation on Linux is a work in process. I couldn’t trust testing my system only on the development Mac. I needed to run it early and often in the deployment environment to insure it did work on Linux. I’m glad I did, because I ran into a couple of Foundation limitations that I had to work around.
My employer has a few different virtual private servers, where we host many projects on each one. Those were not candidates for this project, because I couldn’t disturb any of the other projects hosted on those machines.
I also don’t need much processing power for this server, because the app will never have more than 1,500 users, as that’s how many people can participate in the contest.
I also needed full access to the system, so I could install Swift, and anything else I needed, such as choosing an OS that matched a Swift distribution package.
These requirements lead me to a small virtual private server. There are a number of hosting companies that offer such systems. I chose Digital Ocean, because their base instance is merely $5 a month. With two servers (development and production) and a couple extras, the cost is less than $15 a month. And I’ve heard good things about them. All the details about my Digital Ocean setup is a bit out of scope for this article, so I’ll just say two more things about them. It was fast and easy to sign up for a Digital Ocean account, and I recommend their tutorials for configuring a system the way you want.
Docker is popular right now, and all these frameworks include distribution paths that include Docker. Docker seems like a great tool, and may have been beneficial to this project, but I simply did not have enough time to also figure out Docker. Maybe I’ll be able to add Docker to the mix on my next project.
Of course the HTTP traffic for this app needs to be encrypted. Actually, it probably doesn’t need it, but that option wasn’t even considered. So the next aspect of this project I tackled was getting the Vapor app to use TLS. It wouldn’t work for me. After running out of things to try to fix the server, I asked the Vapor Slack community what they thought, and somebody suggested using nginx as a proxy server, letting it handle TLS. Following that suggestion, I setup my Vapor app to serve to only the local host without TLS, and nginx serves to the world only HTTPS traffic. Without getting into too many details here either, setting this up was just installing nginx with apt-get, copying over the certificates, and editing a configuration file.
To make the app a daemon, an always on background process, I used supervisor, as document in the Vapor online documentation. Since this is a single use server, I’m just running the app from the build folder, inside the project folder, inside the one user’s home folder on this machine. This is a little sloppy, but makes it easy to update and install newer versions of the app.
I need the app to download a fresh set of data exported from the customer every morning. Vapor is designed to react to outside requests, so I didn’t see an easy way to get it schedule tasks. Instead I just added a new endpoint that triggered the update, and had a cron task hit that endpoint with a curl command. For security nginx is configured to hide that endpoint from the outside world.
The expected way to install Vapor and its toolbox is to curl some scripts piped to bash. This feels way too insecure, but I did it anyway, and nothing broke. This resulted in installing the vapor command line tool that lets me:
- Make a new project
- Load dependencies
- Build the app
- Clean the project
- Prepare the database
- Run the app
- Make an Xcode Project
Vapor is written in Swift 3. I’ve been writing Swift code, on and off, since they introduced Swift a few years ago, but Swift 3 is a major change. I’m still getting used to the great renaming of Apple’s APIs. I like the new naming style, and try to follow it for my code, but I’m struggling with translating my knowledge about Apple’s APIs into the new Swift names. I find I’m searching the Objective C names, then seeing what they are called in Swift 3.
Foundation is Apple’s low level framework for the data manipulation parts of an app, but not the user interface. It’s great for writing a UI free REST backend service. With the porting of Swift to other platforms (Linux so far) Apple is making a version of Foundation that is pure Swift and portable to the other platforms. So much of this work is done, that you can almost comfortably code with abandon when writing code on a Mac for eventual deployment on Linux.
Some parts of Foundation are missing in the Linux port. I ran into two API calls that were not supported on Linux. The lesson here is that part of the regular test procedure should include running the app on Linux. It may work perfectly on a development Mac, but then crash when run on a Linux system.
The specific problems I found were in date formatter code. I needed to convert a string to a date, and then figure out what week those dates were in. I could have done this with a few Foundation API calls, but instead I ended up writing a custom date class. It’s not as powerful as the Foundation APIs, but it does what I need on macOS and Linux.
My Vapor app has 40 dependencies not counting anything in Foundation or Swift. You have to download and compile all these dependencies, each of which is being updated on its own schedule. As I’ve argued elsewhere, lots of dependencies are a risk. I find it acceptable for this project, because of its short life. But I’m not sure I could accept this if we were building a backend intended for long term service.
But there may be a reduction in these dependencies. Swift.org recently announced a Server APIs Project. Many of Vapor’s dependencies may be met in the future by these new Swift APIs. Sure, these dependencies will also change, but I expect them to change on the same schedule as Swift, so it’ll be an expected and predictable change.
Fluent is an ORM (Object-Relational Mapping) for Vapor that can map Swift model object to multiple databases. I used a MySQL database, and found it very easy to work with. All I had to do was follow the instructions in the online Vapor documentation.
I ended with a demo. I demoed the Hello World! Project from the documentation, then added a database, and allowed users to sign up, then authenticate against that signup. You, my blog audience, would be better off hitting Vapor U and watch those videos than trying to read a description of this demo.