Catch NSExceptions cleanly in Swift 2 with do/try/catch syntax

Carl Peto
4 min readMar 29, 2016

(TL:DR… the magic NSExceptions extension class is described at the bottom of the post.)

OK, I’ll admit it’s not a very sexy title and falls into the category of “I didn’t know I needed that, did I?”.

But if you’ve got sick of Crashlytics logs that look like this…

For some or other of the myriad minor functions that your app performs, you may, like me, start to think…

“Back in the old days, I used to put an @try…@catch…@finally around code like this. It stopped endless, pointless crashes, the user barely noticed that the relevant function had failed… And it was *fine*. No one died.”

Back up a bit… doesn’t Swift 2 cover us here?

Not so much… it sort of depends. The first mystery, lost in Cocoa/Cocoa Touch/Foundation lore that we need to investigate a bit is the difference between NSError versus NSException.

Those of us with a long history have seen a lot of each of these. Many times we just got used to it and didn’t really notice the difference but Apple was always keen to point out that they were conceptually meant for different things. That difference has become suddenly, wonderfully apparent with modern Swift.

Programs go wrong. A lot. And in any given case, when you call a system/library/framework function/method there could be lot of reasons why it goes wrong, the file you’re after no longer exists (another process removed it), or the directory name you are trying to access no longer exists. Generally most of these would be regarded as errors, the way Swift 2 handles this is a method that report these errors can “throw” instead of returning, underneath the hood it’s actually a different type of return (you can see this best with objective C interop where the method returning false/nil and setting an error causes a throw).

By contrast, many more “severe” problems can be reported and thrown by any of the layers of the application, system libraries or operating system and these are often captured and characterised in objective C with an NSException. The idea is that these conditions are severe, are not the result of edge cases or minority routes taken in healthy program flow but are possibly fatal problems. The implication is that, like asserts, they reflect a serious “mistake” by the programmer. Swift 2 takes the brutal position that these simply cause program termination, unless some sort of exception handler has been installed in the app, such as Crashlytics, so at least the programmer gets a stack dump to help her/him try to fix the bug.

The problem is, there are many layers of program down to the metal and these are not all under the Cocoa/Swift teams’ direct control. To take a canonical difficult example writing to a dead socket can cause a SIGPIPE that will usually kill a naive UNIX program. When this decision was made, pipes started as simple interprocess communication between master and slave processes and a broken pipe was a fatal, rare and terminal condition. When that idiom was translated into network sockets it started to look a bit weak and when it was translated into network sockets connected over a flaky 3G mobile network it started to seem psychotic… user goes under a bridge, app crashes.

To mitigate this, objective c has @try… @catch… to trap exceptions in blocks where we know how to recover (try to write the file, if it fails, fallback gracefully, try to write to the socket, if it fails, patiently try to connect a new socket a few times).

So recently I found myself wanting to add this protection into a Swift program. In the case I had, I was attempting to restore the current state of the app as if a user had logged in. On occasions, due to app upgrades or other unusual but reasonable circumstances, the state could not be restored but that’s just a minor inconvenience, it’s all cached information anyway, so we just clear state and let the user log in again… it’s churlish to crash the app for such a trivial reason.

Since only objective C has this capability currently (using a standard stack unwinding exceptions type mechanism as I understand it), I created a couple of simple (to describe) methods in objective C that take a block to execute, run it in a try.. catch and if an exception is thrown and caught anywhere in this process, create an NSError with salient details and return it to the caller in the format that Swift 2 interop expects. The hard part was trial and error plus getting the syntax exactly right so it looked clean and simple in Swift.

do {  try NSException.Protect {    ...code that might throw exceptions or errors...  }} catch let myErr {  ... handle the errors AND exceptions}

This blog piece is just here to describe reasons and principles. You can probably figure out how to implement it yourself. If you don’t want to, let me know and I’ll attach the NSException category files here with simple instructions. I haven’t quite got the license sorted yet so I’m not pasting it until someone shows interest. :)

--

--

Carl Peto

iPhone app developer since 2008 and mobile/smarphone fanatic