Failure strategies vs Swift optionals

When writing code in any language, there’s a couple of ideals that I always try to follow around error handling:

  1. You should always process all possible error paths and respond accordingly.
  2. If the options are between crashing and getting into an inconsistent state, then crashing is better

These two tenants of error handling were drilled into me whilst at Bromium where I lead the team that built the Mac version of their vSentry security product. Every error, no matter how innocuous it may seem, will happen to you when you least expect it, so you should *never* ignore an error path. As a code author you're always chasing the success case, as that's the functionality your user wants, so it's easy to forget that a function might error or that some input to your bit of code might not be what you expect. One of the things I like about Swift (and to a lesser extent also in Go) is that error handling is made explicit and it’s opt out rather than opt in: if you want to ignore an error you can do, but you have to decided to do that, rather than just forget. Such a wonderful feature of the language (modulo the initial confusion because they used exception syntax for something that isn’t exception handling.

At the same time as doing this, there are points in your code where either you know that a failure case will never happen, or if it does it’s because of a programming error rather than something unanticipated in then input or the environment (or at least you expect some code called before yours will have done any input validation). For these cases it’s perfectly fine to just assert a particular state rather than handling it (but still you must acknowledge it!).

It always amazes me that people disable such asserts in production builds though. On most modern event driven code there’s no performance justification for doing this, so the reason given is that you don’t want your program to crash on the user. But to me the alternative is far worse: if an assertion would fail and your asserts have been removed, then your program is now in a state you never designed for it. If you’re lucky nothing serious will happen, but at worst you can cause the user confusion and potentially data loss (very early in my career I failed to validate that I’d detected the temporary folder on disk correctly, and thus cleared out empty string on a disk, which is the root folder…). Yes, if you crash the user will be disappointed, but you will at least get feedback, hopefully a stack trace, and very quickly a bug fix. Otherwise your code can be mis-performing for years before you realise.

Crashes are obviously bad and to be avoided at all reasonable costs, but if you follow rule one, you should never have a crash, except where something you asserted would never go wrong does. In which case you’ve learned you should have handled that error despite what you assumed. If you don’t like asserts, then write more error handlers, but doing neither is not an option in my book.

I’ve finally come to my peace with the two variants of the optional value unwrapping in Swift, which initially to me seemed like an odd design decision for a language focused on program safety. In Swift if you have an optional variable (i.e., a variable that either contains a value or is null), you can either use the conditional unwrap (the ? operator) or an unconditional unwrap (the ! operator). I was of the opinion that you should always use the conditional version and provide suitable error handlers, and that the unconditional unwrap was akin to never bothering to check return values on functions in other languages. But I've been writing more iOS UI code in anger and it has made me realise that the unconditional operator is actually like an assert: I’m going to unwrap this optional and I assert it will always hold a value. This is very useful for where you have resources loaded from storyboards etc. where you “know” that the value won’t be nil and will point to a UI element but convention requires the variable be optional.

You still need to be fully cognisant of this decision, and distrusting of any code you see where an unconditional unwrap us used until you’ve convinced yourself it’s actually just an assert call, and in general explicit error handling is always better for the user, but I’m glad I’ve now found a place for what used to seem like a Swift feature that went against all the other things that make the language safer.

Why I moved to Hockey App

Until recently, if you were testing iOS apps, there was one third party service that was absolutely essential - TestFlight. TestFlight is a web service that helps you manage all the apps you develop and your app testers, and makes it easy for testers to install your apps on their iOS devices (something Apple have made quite tricky otherwise). Over time TestFlight added more and more functionality, making it easy to retrieve crash logs, add checkpoints to see which features were tested and which weren't, and provide reminders to testers when new versions are available. TestFlight was a godsend to both developers and testers alike. And on top of everything, it is totally free - how much better can you get?

However, I recently came to find TestFlight wanting, and switched to an alternative paid for service, HockeyApp. At a fundamental level HockeyApp does a fairly similar set of things to TestFlight, but is a little less slick than TestFlight, and costs money. A few people have asked why I switched, so here's a quick summary of where HockeyApp wins over TestFlight for me.

Multiple platform support

TestFlight only supports iOS, but of late I've been working on a couple of things for OS X, and HockeyApp supports testing OS X apps in addition to iOS (it also claims to support Android development, but that's not something I've had cause to investigate yet). In general, testing on the Mac is easier than testing on iOS, as Apple have no special technologies in place to limit how you distribute test applications on the Mac, but even here HockeyApp make the process much better with their help than without.

Sparkle is an all but ubiquitous open source library for rolling out app updates on the Mac, or at least it as before the Mac App Store took over that duty. Sparkle will sit in your app and monitor a appcast feed from a server (much like an RSS Feed) to check for updates. HockeyApp supports using Sparkle - when you upload test builds they'll appear in a private appcast feed, and if you tell your app to look at that all your users will get notified the moment there's a new update, ensuring everyone is up to date.

Even if you intend to ship through the Mac App Store, which doesn't allow you to use Sparkle, you can use Sparkle during testing and then remove it when you submit the app to Apple - it's just a huge time saver.

And needless to say, that Hockey App supports both iOS and Mac means I can just use one service rather than two, which is always good.

Uptime

After almost a year of flawless service, TestFlight had some unfortunate downtime recently. Running a web service is difficult, so I appreciate that sometimes things go down, and they have my sympathy there. But I hit a situation where TestFlight went down twice for the better part of a UK working day (they're a US company, so they're most likely asleep at that point), and during the second event there was no acknowledgement that I could see from TestFlight that anything had gone wrong, and during that time I couldn't get important test builds out to my clients, leading me to have to apologise to people for something beyond my control.

This is where the \"free\" suddenly is less appealing. I don't really feel I can complain when something offered for free goes away. It's not that you get what you pay for, but it's more that there's no way in which TestFlight are particularly beholden to users when the users don't pay.

HockeyApp do charge, which makes the relationship much easier to understand. HockeyApp's uptime has been thus far been very good, but of course all web services will have times when someone goes wrong beyond anyone's control, so I don't expect HockeyApp to be above the occasional outage. But I feel they'll take it more seriously given that I'm paying for the service.

Integration with Xcode

I gather TestFlight does something similar now, but at the time when I switched I still had to upload apps to TestFlight by hand via a browser upload. HockeyApp have a little app for the Mac that you can integrate with Xcode as part of the app archive process, which will upload new builds to HockeyApp directly. Combining this with automatically generating version numbers from git means I now don't need to lift a finger when getting new test version of my apps to testers.

Nothing is prefect

This all makes it seem like HockeyApp must clearly be the way and anyone that uses TestFlight is in the wrong, but we must give TestFlight its due here - there's still some things that TestFlight do better than HockeyApp. TestFlight's website is just generally a little more slick than HockeyApp - be it setting up new apps, adding users, and so forth. TestFlight's library for getting crash logs, adding checkpoints from apps, and grabbing new builds is just a single library - you need to add two for HockeyApp and it's just a little bit more involved. But none of this adds up to much more friction in the long run, but it might be enough to keep some people from switching.

I started trying out HockeyApp just for my Mac OS X projects, but at the end of the day I switched wholeheartedly when I was unfortunate that their downtime coincided with a critical time in my app development and was unable to reach my testers twice in a month. I suspect that's atypical, and you should have a look at both services for yourself, but I think it shows that is just how important to developers apps like TestFlight and HockeyApp are now - they're essential infrastructure for developers, and we're severally impacted when they go away. I'm happy to pay for a service like this in order to try to ensure it's reliable - I just can't work without it.

Update: I got a nice note from the people at HockeyApp letting me know that they'll be moving to having just a single library for managing in app updates and crash logging etc., which is great news.