Swift and Cocoa Error Handling
I’ve not see much written about how to do real-world Cocoa error handling in Swift. That is, you’re calling Objective-C methods with NSError **
parameters and need to return an NSError
back to your caller. This is very common with Cocoa.
NSError
is unwieldy, especially when you must consider that the output parameter could be NULL
. The way I handle this in Objective-C is to use a macro to reduce the amount of error handling code that’s visible:
NSError *e = nil; NSString *string = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&e]; MJT_REQUIRE(string, e, error); [string doSomething];
This leaves straight-line code, with no indentation, for the normal case. The macro, inspired by Apple’s AssertMacros.h, does nothing if its first parameter indicates success. Otherwise, it propagates the second parameter (the local error object) to the third parameter (the passed-in error pointer), but only if the latter isn’t NULL
. It can also add some contextual information to error object, to track the source of the error. Lastly, it returns nil
to indicate failure to the caller.
(I also have MJT_BOOL_REQUIRE()
, which returns NO
instead of nil
, and MJT_REQUIRE_PARAMETER(param, error)
, for when a parameter can’t be nil
. This is sometimes preferable to raising an exception with NSParameterAssert()
, and it is otherwise very verbose to construct an appropriate NSError
to return.)
Swift doesn’t have macros, so it’s not clear to me how this sort of pattern can be encapsulated. It looks like the code would be something like this:
var e : NSError? let possibleString = NSString(contentsOfURL:url, encoding:NSUTF8StringEncoding, error:&e) if possibleString == nil { if error { error.memory = addErrorContext(e, context) } return nil } let string = possibleString as NSString string.doSomething()
I don’t like the way this code looks. I could probably write a helper function to make it something like:
var e : NSError? let possibleString = NSString(contentsOfURL:url, encoding:NSUTF8StringEncoding, error:&e) if failed(possibleString, e, error) { return nil } let string = possibleString as NSString string.doSomething()
But I don’t see how to hide that if
and return
. And then there is the matter of having to create a second variable for the non-optional string if you don’t want to add !
after each use. I hope I’m missing something here, because it looks like Swift is making basic code more tedious without providing any real benefit.