Server-side Swift: Making Canopy (1/6)

Max Howell
Nov 6, 2018 · 8 min read
Image for post
Image for post

Recently I built and released Canopy, a service that provides instant push notifications for activity on your GitHub repositories with apps for Mac and iOS.

I haven’t worked on anything server-side for years, setting up the server and writing the server-side code was really fun; it was refreshing to take a break from event-driven app-like programming and do something different, but still I got to write it in my favorite language: JavaScript… only joking, Swift.

Both client and server-side are Swift.

The amount of information out there regarding server-side Swift is low, which is not surprising, but I’m here to tell you it’s not that much of a stretch if you already know Swift and also know your way around the macOS command line.

Swift is Great

This week I am focusing on some specifics of why Swift was great for the server. Obviously protocol-driven-development, functional programming, optionals, the error-handling model, etc. make Swift great in general, but I won’t call those out specifically. Certainly these things helped make writing Canopy a joy.

There is no JSON

Image for post
Image for post

Create my struct, transmit, receive my type-safe struct. It feels amazing.

Intrinsic type-safety cross-process with barely any work from me was super comfy and made the 1,000 miles between me and my server seem no different to passing data between view-controllers.

However the lack of dynamism means even adding a single parameter to these structs means versioning my endpoints or making new parameters Optional. It’s not all roses, but frankly, versioning has worked well and means my code for interpreting incoming data is not a logic bomb of if-statements, it is carefully, separately encapsulated and tested, type-safe endpoints.

Uptime

One crash: Swift is amazing. You have to actively aim to write Swift code that isn’t robust. And of course Swift gives you robust code and high performance.

Great Developer Experience

You can develop and test your server code on your Mac while simultaneously running your client-side app and having them communicate together—just using Xcode.

To do this is currently more clunky than we want, you have to get SwiftPM to generate you an Xcodeproj (swift package generate-xcodeproj) from a Package.swift that describes your server-side app, and then you embed that in your .xcodeproj (drag and drop) for your client-side apps. Xcode knows what to do after this. Doing this also makes it easier to share code between your various server and client apps.

Since SwiftPM doesn’t really support iOS yet you’ll still need Carthage for your iOS app dependencies, but if you make a macOS app you can use the dependencies that SwiftPM fetches if you’re using similar dependencies, which you probably are. This is non-ideal for sure, but it will be better by 2020 (so we’re told–better Xcode integration is coming).

You have to use Xcode (I guess you could probably use AppCode) at this time since pleasant use of Swift absolutely requires auto-completion. Apple acknowledge this is a hindrance to wider adoption however and have recently committed to providing a Language Server Protocol service so other editors and other platforms can have a first-class Swift experience for their developers.

Running your code server-side is as simple as an rsync followed by swift run foo. Obviously you should have a better deployment process than this, but I’m trying to demonstrate how simple it is.

The Code Works First Time

Cross-platform Errors

Still you often have to write the Codable implementation manually since you probably are using Swift’s (superb) enums with associated-values and Swift cannot auto-generate the Codable implementation for these. So again, not all roses. Sourcery could probably solve this (update: it can).

Cross-platform Routes

Image for post
Image for post

Unicode

Foundation

Though admittedly you could pick Java here, but I’m focusing on alternatives that are more modern than the (relatively) ancient Java, which as a consequence lacks modern language features that we want.

Foundation has thorough solutions for complicated scenarios like, locales (et. formatting, currencies, locale-aware-string-sorting etc.), dates, timezones, currencies, networking, IPC/XPC etc., etc. It is awesome.

Simple Integration with C Libraries

And for sure, cryptography is too important to rely on something that isn’t old and battle-tested.

Performance

The Bad

Swift on Linux is not as complete as Swift on macOS. For example Linux Foundation is incomplete. During development I would experience low uptime because URLSession has a few NSNotImplemented fatal-errors that would crop up non-determistically. If you go look at the sources you’ll see Foundation’s network layer for Linux is built on libcurl and it’s incomplete.

I migrated my networking away from Foundation instead using the libraries that the server platform I chose provided (they provide it because Foundation is spotty on Linux).

This is unfortunate, but it’ll be a thing of the past over the next few years and is a golden opportunity to contribute to Swift itself, which I would do in this case but it seems that Apple plan to fix this themselves using the recently released Swift-NIO.

Still, debugging cross platform code where it behaves differently on different platforms is a bitch, and debugging on Linux is a super-bitch. I couldn’t get the debugger to work at all, and just gave up instead using gross print statements and something I usually lack: patience.

Some Foundation bridging is lacking on Linux (where you would as NSSomething) and you will only know when you compile on Linux, so that sucks, but it is a fairly rare problem.

Having to write Codables for all the JSON I get from GitHub was also a PITA (GitHub provide 43 different webhook payloads), but at the end of the day I am much happier to have type-safe structs than the quick-coding alternatives that JavaScript and even ObjC allow when operating with JSON. And well, let’s be clear: writing Codables is a pretty fast exercise in translation; the Swift compiler does most of the work.

The worst parts of Swift on Linux are coming in follow up weeks because they concern build-systems and general Linux woes.

Hey there!

Image for post
Image for post

I’m Max Howell and I want to be full-time making, writing and being all about open source. I’ve been doing open source for more than 15 years, and you probably already use some of it (Homebrew anyone?). I need your help to continue, any contribution is most welcome. Thanks so much.

https://patreon.com/mxcl

Part Two

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store