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:

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:

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.

12 Comments

If you could rewrite the post with correct code (and I don't care about "doSomething" not being implemented), you might get more discussion or help.

But you're right, error handling isn't quite what I'd expect from Swift, given that there are Optionals and tuples now. Methods like contentsOfURL should really return something like a tuple of (NSString?, error?).

Returning a tuple would make this a little cleaner, but you would still need to have an if and a return after every line that could return an error.

@Michel Yes, the local error variable is not necessary in this example. I included it because in real code I generally like to have it. This lets me inspect the reason for the failure, log the error if necessary, use it inside a higher-level error object, etc.

Thank you for reminding me about implicitly unwrapped optionals. I guess that’s what we’re meant to use here, although then the compiler doesn’t check that we handled the possibility of failure. So how is the type system helping us? And I’m typing more (let string: NSString! vs NSString *string), too.

The caller can then pattern match on the return value's class to determine which block of code to execute next: the one that processes the string or the one that reports the error.

As you say, lack of macros makes it difficult to apply such transformations yourself. Although given the choice between no macros and C "macros" (muck rows?), I think I'd still rather have the former than the latter.

Besides which, given Swift's stated goal of being a first-class Cocoa client, this seems like the sort of pattern-driven transformation that would be best baked into the language itself.

It's just a shame Distributed Objects' in/out annotations never became a standard notation in all method signatures, as that would've made it damn near trivial to generalize such transformations so that they could apply to all argument-based return values.

isn't the absolute worst of all options to be slapped with a big fat clue stick before they get any further. That sort of painful non-Swiftian anti-pattern that is going to drive everyone up the wall with its clunky lame and fail, giving Swift a false reputation for suckage when the real problem is Swift noobs (i.e. everyone right now) screwing up due to their own preconceptions, prejudices, and misunderstandings about how the language and its idioms actually work.

@has That would be the Swift way to do it, but it doesn’t (yet?) automatically transform the Objective-C APIs like that.

I like pattern matching, but I don’t see it as a good solution for handling errors for a sequence of calls that could fail. It seems even worse than using if (with early returns for errors) because you end up with many levels of indentation for the normal case. Have you seen code that does this and actually looks good?

The fundamental problems with NSError are that it mucks up your code and puts the error at the lowest level. All you could get out of the example above is that you failed to create a string with the contents of the URL. So? Were you trying to read a config file? A data file? A remote file? An XML stream? No way to tell. You have this error message that is virtually useless. Because Cocoa has broken exceptions, there is no way to associate an error with the operation you were trying to perform.

I'm going to have to come up with an alternate error handling strategy. I can't use exceptions and I'm not going to use NSError. How about some global structures that can keep track of major waypoints during the execution of your program. It can keep an NSError ** handy and you can pass that to any method. If the method returns false you can trigger your waypoint watcher which can log messages, alert the user, recover and try again, depending on how you've setup your waypoint.

@John The syntax for NSError is not great, but I don’t really understand your other criticisms. A good error will include the URL of the file that you were trying to read, why it failed, etc. And, as you pass the error up through your code, you can annotate it or wrap it to associate it with higher level operations. Seems to me that it would be more of a pain to do this with a global watcher, especially if threads/queues are involved. However, I do like the idea of having an error-handling policy object that can be configured or switched out at runtime.

That is exactly the problem with NSError. I don't want to have to weave low-level error handling all throughout the logic of my software. Any particular NSError only has meaning where it occurred and only for the programmer while debugging. It has no value for the user or programmer after the fact. Users need meaningful errors in the context of the operation they were attempting to perform. Programmers need a stack trace after the fact and to keep messy error handling code out of sight during development.

I would have preferred an elegant, supported error management architecture like exceptions. I like the waypoint idea. I am going to write that up.