A couple of months ago I came across a post about server-side Swift frameworks — a performance comparison of four Swift server frameworks against each other and against Node.js. Performance caught my attention. Judging by the results, Perfect came out on top. A month later the author posted another comparison of performance, but this time tested not on macOS, but on Linux. Perfect was in the lead again.
This weekend I decided to finally get my hands on it properly. Before that I took a quick look at the four frameworks being compared: Perfect, Kitura, Vapor and Zewo. I did not have many requirements for a framework; besides standard request routing and returning responses, I needed:
- Templating support, so it can return not only JSON, but HTML too.
- Support for network requests, since Foundation still has nothing ready for networking.
- Preferably ready-made libraries for working with different databases.
Naturally, all of this needs to work under Linux.
Kitura
Kitura is one of the most advanced frameworks. There are libraries for MySQL, CouchDB, MongoDB and others. The only problem is that the performance test results are disappointing — it trails behind. On the other hand, it is being built by mighty IBM, which not only develops it but also actively contributes to Swift itself. There seems to be a library for network requests. There is templating support too, but things are bad there. There is Mustache, which only works on macOS and was only moved to Swift 3 three days ago. There is also Stencil, but when I looked at it there was some trouble there as well, related to the fact that it still could not be used with the latest Swift and Xcode.
There are a ton of repositories on GitHub with different libraries and components for Kitura, while documentation is almost completely absent. Yes, there are examples, but they are all primitive; as soon as you realize you want to do something more complex, you hit a dead end. Maybe it can do all of that, but digging through the sources of a bunch of interdependent libraries feels like an unjustified waste of time.
Today I decided to make a test project with it. Seeing that they have a library for working with CouchDB, I wanted to check whether it works (because the one I wrote in an evening does not, more on that below), but swift build could not even resolve dependencies after I added the Kitura-CouchDB dependency, which depends on some logger, which depends on yet another logger. All from IBM. It is some kind of hell when all of it comes from one vendor and still does not work together. I opened an issue for them on GitHub, and that was the end of my attempt to code anything with it. Putting it aside for now.
They only started this in February of this year, so I hope they will polish everything properly; IBM certainly has the resources for that. I literally watched a video from a Swift conference today where they promote Swift backend development and their framework.
Update: To get the project to build, I needed to specify version 1.1 for the dependency in Package.swift, as they told me in response to my GitHub issue. Maybe something will work out now. Though the aftertaste remains.
Zewo

You open the GitHub page and once again see another primitive request-routing example. If you dig through the list of repositories, you see that there seems to be a PostgreSQL client, an HTTP client for network requests, and a bunch of wrappers around C libraries.
Again, there is no documentation, and its popularity is much lower judging by the number of stars on GitHub. When you visit the project website, you want to see documentation, not a one-page Hello World.
There is Mustache for templates, but it still has not been moved to Swift 3. It is unclear how actively it is being developed; there seem to be many components, but if they cannot keep up with new Xcode and Swift releases that shipped a month and a half ago, it does not inspire confidence. At all. You cannot even try it because of the lag behind.
Vapor
It looks presentable. There are libraries for MySQL, PostgreSQL, MongoDB, its own ORM. There is an HTTP client for requests. There is the Leaf template engine.
The documentation looks excellent. There are text and video tutorials on a separate site.
I have not had time to try it yet, but I really want to, despite the fact that it did not show the best results in the performance test.
Update: a post about Vapor.
Perfect
The most popular Swift framework so far, at least judging by the number of stars on GitHub. It was the leader in the performance test, so naturally this was the one I wanted to look at first.
The site has great documentation for everything it offers. Since it is all collected in one place on the site (and duplicated on GitHub), I immediately saw that it has a wrapper over cURL for network requests, database libraries for SQLite, MySQL, PostgreSQL, MongoDB, and its own ORM. This was the first framework where I saw everything I needed (because some of the others simply do not have decent documentation), so this is the one I decided to try.
How I Tried to Write a Backend in Perfect
Perfect handles basic routing perfectly. The syntax is simple. I immediately decided to try building an API with it. For the API I wanted to use CouchDB. Since they do not have a ready-made library for it, I decided to quickly sketch my own, because CouchDB can be used through plain HTTP requests, and the framework ships with a wrapper over C cURL.
In a couple of hours I sketched out a method for fetching data through a GET request. I tested everything on macOS and, hooray, it all worked. I implemented the methods I needed and decided to try writing something to the database as well.
That is where the pain started. On macOS the data simply would not write to the database. That is, I send a plain POST request:
let curlObject = CURL(url: url)
curlObject.setOption(CURLOPT_HTTPHEADER, s: "Content-Type: application/json; charset=utf-8")
curlObject.setOption(CURLOPT_POST, int: 1)
curlObject.setOption(CURLOPT_POSTFIELDS, s: jsonString)
curlObject.perform { code, header, body in
// code
}
CouchDB kept returning an error saying that invalid UTF-8 JSON was being sent. I pasted the string I was sending to the database into a separate REST testing app and there the data was accepted just fine. The strangest part was that if I made many requests from my code, then 1–2 requests with exactly the same data would be accepted and written by the database, while the rest failed. I never understood what the problem was. Either someone has a bug, or I do not know how to use CURL.
I spent a long time fighting this problem, and in the end I decided to just forget about it and upload what I had already done to my Ubuntu Server. That is where the new adventures began.
Something that builds perfectly with the swift build command in the console on macOS may fail to build on Ubuntu. Moreover, something that builds in Xcode may fail to build in the console on macOS.
After spending a lot of time on trouble spots, mainly related to pulling data out of Dictionary values with several nesting levels, I finally made the code compile on Ubuntu.
It built, I sent a request — and the app on the server crashed. I never solved that problem either, and on top of that I was upset that something working perfectly on macOS simply crashes on Ubuntu, even though Swift 3.0.1 was used on both.
After that there is a feeling of disappointment. I spent so much time on it that I do not really want to give up.
But I did make one pleasant observation: essentially I rewrote the API for my app that I had previously done in Node.js. The Node.js API built with Restify uses 35 MB of memory. The same API written in Swift grew only to 5.2 MB of memory on macOS. On Ubuntu it starts at 3.5 MB, but I could not check whether memory consumption increases, because the binary simply crashes whenever you send a request.
Conclusion
Once again, we have to wait for new Swift versions and new framework versions to build something real with them. Also, at the end of October Apple formed a working group to improve tools for backends written in Swift, so there is hope. While writing this post I took another close look at Vapor and was very pleasantly surprised. That will be the next one I try.
So, in summary:
Kitura does not build and showed poor performance results, but IBM is actively working on it.
Perfect builds, but what I wrote does not work on Ubuntu even though it works on macOS; apparently we need to wait for new Swift and Perfect versions.
Zewo is lagging behind, and I am not placing much hope in it at all.
I urgently need to try Vapor. (Update: I did)
If your code compiles on macOS, that does not mean it will compile on Ubuntu. If it compiles on Ubuntu, that does not mean it will work there, even if it works on macOS.