Server side applications in Apple’s Swift
In 2014, Apple announced the release of Swift, a new programming language for all their platforms. Their programming language of choice on iOS and OSX has always been Objective-c, a language which is a bit dated (it predates C++) and as it has had new features (and syntaxes) bolted onto it every few years, it carries quite a bit of baggage. It seems I wasn’t the only one with this opinion, as the release of swift was greeted with great enthusiasm, and has been adopted very rapidly.
Swift combines all the features that are fashionable in a general purpose language today, without the feeling that they were bolted on after the fact. While building an iOS client for our customer Gerimedica in swift, I found myself wishing I could use this language on the server side as well as in the client. At WWDC 2015, Apple announced the intention to open source the language, and release a Linux version, so it looked like it could become a reality. Since december 2015, the sources have been available on github, and builds for OSX and Ubuntu are made available roughly twice per month.
PerfectLib
A number of groups and companies saw an opportunity to be among the first with something that was obviously going to be big. One of the first was PerfectSoft, a startup that aims to be the one big framework for all your server side development in swift. They started building their framework as soon as the open source release of swift was announced, and have been advertising their product everywhere. Because they started development before anyone outside Cupertino had a good idea what the release would look like, it only worked on OSX at first, and it didn’t use the Swift Package Manager, the intended default build and dependency management tool for swift. At the time, the framework compiled to one big binary, that you had to include in your build manually. They have a beautiful website and good documentation, but it just wasn’t working when I tried it. I intend to try this framework out again at a later date.
IBM
The biggest player (other than Apple) to openly jump on the swift bandwagon is IBM. As soon as the open source release of Swift was announced, IBM announced the Swift Sandbox, their Swift based version of google’s golang playground. It is a web based repl that can be shared online by sharing a URL. Cool, but not extremely useful, as unlike go, swift already comes with a repl. The real significance of this is not the swift sandbox itself, but the message that IBM is interested in this technology and intends to be involved. IBM isn’t the kind of company to back technologies just because they like them, so they either see an opportunity, or a potential strategic interest. At the moment, IBM’s swift related activities seem to be associated with their PaaS solution BlueMix, so they are likely working on the Swift / IBM version of google’s app engine for go. IBM offers its own web framework for swift: kitura. Kitura turns out to be less than trivial to install and for now somewhat bare bones, but as this is IBM, it is worth dedicating another blog post to it at a later date. Also check out their overview of the most popular, most active and most essential open source projects on github for swift.
There have been rumors all over the internet that google would be considering making swift a first class language for android. As far as I can tell, all of these rumors can be traced down to one article on thenextweb.com, which states that ‘sources have told…’ For now, there is no indication that these rumors are anything more than rumors. It’s not clear what would be in it for google, now that their own mobile platform has become so successful. It is already possible to run swift code on android, but there is a very long way to go from there to writing an entire android app in swift.
Zewo
The next framework I tried was Zewo, a project started by Paulo Faria. I immediately liked the architecture: a package based approach, tied together with the Swift Package Manager. You could pick and choose the elements you wanted and simply include the dependency in the Package.swift file, and leave out the parts you don’t need. Let’s build a minimal server side application with Zewo. Installing zewo on Linux turns out to be very easy.
Download and install swift from swift.org. Note that these examples use the DEVELOPMENT-SNAPSHOT-2016-02-08-a snapshot. This is relevant, because at this time, swift 3 is a moving target. It is undergoing rapid development, and every new snapshot released is likely to break your builds, with or without a deprecation warning. Kyle Fuller made swiftenv, an elegant tool for managing installations of multiple different builds of swift. I recommend using this tool to install and manage development snapshots of swift:
swiftenv install DEVELOPMENT-SNAPSHOT-2016-02-08-a ln -s ~/.swiftenv/shims/* /usr/local/bin/
Make sure clang and libicu-dev are installed.
sudo apt-get install clang libicu-dev
Install Zewo:
echo "deb [trusted=yes] http://apt.zewo.io/deb ./" | sudo tee --append /etc/apt/sources.list sudo apt-get update sudo apt-get install zewo
Make a directory for your project, and in that directory type:
swift build --init
This will create the basic structure for a swift application managed by SPM. Paste the following code into Sources/main.swift:
import HTTPServer import Router let router = Router { route in route.get("/hello") { _ in return Response(body: "hello world") } } try Server(responder: router).start()
Paste the following code into Package.swift:
import PackageDescription let package = Package( name: "HelloWorld", dependencies: [ .Package(url: "https://github.com/VeniceX/HTTPServer.git", majorVersion: 0, minor: 3), .Package(url: "https://github.com/Zewo/Router.git", majorVersion: 0, minor: 3), ] )
And compile and link the project by typing:
swift build
The swift build tool will start pulling in dependencies, and will compile and link the project. The build can take a surprisingly long time, which is an issue I will get back to later on.
When the build is done, you can find the executable at
.build/debug/HelloWorld
Just run it. No JRE, application server, servlet container or other middleware needed. You should see an ascii art zewo logo and the message that our application is running and responding to port 8080. Let’s try it out:
curl http://localhost:8080/hello hello world
That’s quite nice for less than 10 lines of code. Let’s add some code to make it a slightly more elaborate REST service:
import HTTPServer import Router import ContentNegotiationMiddleware import JSONMediaType let contentNegotiation = ContentNegotiationMiddleware(mediaTypes: JSONMediaType()) let router = Router { route in route.get("/hello") { _ in return Response(body: "hello world") } route.get("/hello/:name/:location") { request in if let name = request.pathParameters["name"], let location = request.pathParameters["location"] { let data : InterchangeData = [ "name" : "\(name)", "location" : "\(location)" ] return Response(status: .OK, content: data) } return Response(body: "hello error world") } } try Server(middleware: contentNegotiation, responder: router).start()
Add the following dependencies to Package.swift
.Package(url: "https://github.com/Zewo/ContentNegotiationMiddleware.git", majorVersion: 0, minor: 3), .Package(url: "https://github.com/Zewo/JSONMediaType.git", majorVersion: 0, minor: 3), .Package(url: "https://github.com/Zewo/InterchangeData.git", majorVersion: 0, minor: 3),
Build and run.
curl -s http://localhost:8080/hello/rik/amsterdam | python -m json.tool { "location": "amsterdam", "name": "rik" }
At last count, there were over 50 Zewo projects available, including HTTP(S)client and server, JSON serialization, support for various databases (MySQL, PostgreSQL and MongoDB), File IO, OpenSSL, an implementation of Moustache, an SQL query DSL and ORM, Websockets, support for ZeroMQ, logging, basic authentication and more. Not all these projects are at the same level of completion. As of the time of writing this blog, the HTTPSServer implementation is broken, and MySQL support tends to lag behind because the developers prefer Postgres.
Venice
The star attraction of the Zewo framework is Venice, a CSP implementation for Swift, based on Libmill. Libmill and Venice offer ‘Go-style concurrency’, which is true to a point. The programming model is very similar. It is trivially easy to make huge numbers of lightweight coroutines run concurrently, and context switching is very fast. Unlike go, libmill and Venice run all coroutines in a single thread, at least for now, so this should not be mistaken for a tool to easily parallelize your code. I can recommend Rob Pike’s presentation on the subject if the difference isn’t clear.
Here is, for example, an implementation of the famous joke algorithm SleepSort using Venice:
import Venice import Glibc let channel = Channel<Int>() for i in 1...100 { let number = random() % 5000 co { nap(Duration(number)) channel <- number } } for msg in channel { print("received \(msg) from channel") }
It starts 100 coroutines that each wait a number of milliseconds before writing said number to a shared channel. At the end, the numbers are read from the channel in sorted order. It’s a very easy to use, easy to reason about and lightweight model for concurrent programming.
All of the Zewo networking projects depend on Venice for concurrency. For now, that means they are single threaded. To facilitate parallelism, server sockets are bound with the SO_REUSEPORT option, which allows multiple processes to bind to the same port. The OS will then take care of balancing connections between these processes.
Why is the build time for our hello world app so long when compared to, for example, the equivalent written in go? Part of the answer is in the different decisions Chris Lattner and Rob Pike made for the languages they designed. For google, reducing the build times of their projects was one of the main design priorities. From the somewhat bare bones syntax of the language itself to the design of the standard libraries, usability and to some extent functionality has been sacrificed in order to reduce build times. Standard libraries typically don’t contain functions that encourage you to pull in a dependency just to save you from typing or copying a few lines of code. Try looking for a round() function in the math package, for example.
For Apple, a great number of features had a much higher priority than build times: compatibility with existing Objective-C code being one of the most important. If Swift lacked features that existing Objective-C coders expected, or if it couldn’t work well with existing Objective-C code, it would never be adopted. Another major design decision was ease of use, presumably to keep development for iOS from becoming more expensive than android development because of the learning curve of Objective-C compared to Java. I believe they simply reached a point fairly quickly where they decided that compilation times were good enough and other priorities took over.
The compile and link time of the Swift sources isn’t the whole story. Try running a build in verbose mode to see what happens:
swift build -v
There seems to be an awful lot of repetition going on there. Let’s count the top 10 most repeated statements:
swift build -v | sort | uniq -c | sort -n | tail -n 10 31 /usr/bin/git -C ~/Hello/Packages/URI-0.2.0 config --get remote.origin.url 33 /usr/bin/git -C ~/Hello/Packages/Data-0.2.2 config --get remote.origin.url 34 /usr/bin/git -C ~/Hello/Packages/CURIParser-0.2.0 config --get remote.origin.url 36 /usr/bin/git -C ~/Hello/Packages/Venice-0.2.2 config --get remote.origin.url 40 /usr/bin/git -C ~/Hello/Packages/Stream-0.2.0 config --get remote.origin.url 42 /usr/bin/git -C ~/Hello/Packages/InterchangeData-0.3.0 config --get remote.origin.url 42 /usr/bin/git -C ~/Hello/Packages/MediaType-0.3.1 config --get remote.origin.url 44 /usr/bin/git -C ~/Hello/Packages/CLibvenice-0.2.0 config --get remote.origin.url 46 /usr/bin/git -C ~/Hello/Packages/IP-0.2.1 config --get remote.origin.url 49 /usr/bin/git -C ~/Hello/Packages/System-0.2.0 config --get remote.origin.url
The Swift Package Manager is based on git repositories. Those can be projects on github, or they can be local directories on your build machine, but they are git repositories. Why does SPM ask the remote origin url of every dependency dozens of times, starting /usr/bin/git a total of 558 times for such a simple program? It looks like SPM currently doesn’t work well with the package based design decisions the authors of the Zewo framework made. Every dependency is checked, then every transitive dependency, then every transitive dependency of those etc. A Zewo project typically depends on a number of other Zewo projects, each responsible for one small task. They seem to be working on fixing this problem.
Vapor
One final server side Swift framework worth mentioning is Vapor, a project started by Tanner Nelson. When I first tried it out, I had a difficult time getting it to work on Linux, but it has been developing very rapidly, and by now it is only a matter of executing the build script. The build script is written in Swift, which illustrates another cool feature of Swift: it can be used both as a compiled language and for scripting. Because of time constraints, a detailed description of the Vapor framework will have to wait for another blog post.
Fragmentation
At this time, the software landscape for server side Swift is quite fragmented. In an attempt at keeping the fragmentation from escalating further, the makers of Zewo and Vapor decided to launch OpenSwift, an effort to establish some standards and protocols that, as long as you follow them, should allow packages from different frameworks to be used together. It would be a good thing if this succeeds, but ultimately everyone will go with standards set by Apple. As of today, there are none, but I suspect there will be either an official Apple framework or an Apple equivalent of the JEE standard that third parties are free to implement. It is possible that Apple considers this not to be their field, in which case there’s a good chance IBM’s offering becomes the defacto standard. Until that happens, if you’re considering starting a server side framework for Swift, it’s worth looking at OpenSwift to make your project interoperate with others.
Conclusion
Swift is gaining a great deal of traction. An open source community is rapidly developing around Swift, including a number of projects that do not depend on iOS or OSX. The language seems to have been designed to appeal to as many developers as possible. You can use it in an object oriented style, a procedural style, based on elements of functional programming, or any mix of these paradigms that suits you. It can be used as a compiled language or for scripting. The syntax is immediately familiar and to a large extent readable for any Java or C# developer. Two years after its release on Apple platforms, Swift is already well on its way to pushing Objective-c out of the way for new projects. On the server, there is more competition.
It is almost inevitable that Swift will find success as the server side backend to iOS apps. I don’t see Java or Python being pushed out any time soon, but there is clearly a niche where Swift could be used very effectively, namely for writing microservices. There is no need for a separate JRE or middleware. Your project just compiles to a self contained statically linked binary. Once the tooling for management is there, I can imagine such binaries to be easier to manage and a lot easier on the resources than the equivalent based on Java.
Ready for production?
As of May 2016, Swift is not ready for production on the server. After the various frameworks made their projects work with the Swift Package Manager, Apple decided the SPM was not going to be ready on time to make the 2.2 release, so they removed it from the 2.x branch. The SPM will be part of the Swift 3.0 release. This means that the frameworks discussed in this blog all depend on the development snapshots, every one of which is likely to break your build. Swift 3.0 is scheduled for release in the fall of 2016.