What’s new in Swift 2: throw

Error handling is hard. Error handling is tedious. And what happens when something is hard and tedious? It gets ignored.

How many times have you done something similar to this?

NSData *JSONData = [ NSJSONSerialization dataWithJSONObject: parsedLockStatus options:0 error: NULL ];

instead of this?

NSError *error = nil;
 
NSData *JSONData = [ NSJSONSerialization dataWithJSONObject: parsedLockStatus options:0 error: &error ];
 
if( !error )
{
// Do your fancy stuff
}

Riiiiiiight, you are the only one that never did that.

Error handling sucks

Let’s be honest. Error handling sucks, big time. Even thought the Cocoa frameworks (those on Mac OS X in particular) have always provided great resources for error handling and, in particular, error recovery.

But, you know, exhibit A.

So, here comes Swift 2 to the rescue, introducing something that will make roll the eyes to our Java-oriented friends in the house: throw

throw does not suck that bad

Yes, I know, I know, Java has been throwing exceptions for years, and Java this and Java that. But, first, Swift throws does not have any performance side effects, opposing to Java exceptions, (meaning, your program is not going to slow down to a crawl when you try to throw one) and second, and most important, using throw, and its sidekicks try and do catch, you can make your code intent extremely clear and obvious to anyone reading your code (including yourself in a couple of months time).

So, how does the awesome shinny new thing works? Actually, it is quite simple.

If you want to Do Things Properly(TM), the first thing you would need to do would be declaring your error type, which should conform to, surprise, ErrorType. Why would you ever want to conform to ErrorType? Well, because that’s what NSError conforms to. Wink, wink, nudge, nudge.

So, again, your error:

enum Error: ErrorType {
    case Dummy
}

Remember the previous post? We had a function that bailed out as soon as a set of sanity preconditions where not met. Well, let’s rewrite that one to, instead of just bailing out, throwing one of your shinny new errors.

func divide(x: Int?, y: Int?) throws -> Double {
    guard let x = x, y = y where y > 0 else {
        throw Error.Dummy
    }
     
    return Double(x)/Double(y)
}

Please note that, in theory at least, throwing an error is not supposed to be that different from an early return. Specially in terms of performance. In other words, throwing errors is the cool thing to do now.

Now, clients of this function can do this:

do {
    try divide2(10, y: nil)
} catch Error.Dummy {
    print("What are you doing, you idiot!?")
}

Note how you must add the try keyword when calling a function that throws something. Explicit much?

Final words

Now, as usual, reader discretion is advised. Use this at your own risk. It is not that hard to end up with code like this:

enum Error: ErrorType {
    case Zero
    case Negative
    case Null
}
 
func divide(x: Int?, y: Int?) throws -> Double {
    guard let x = x, y = y else {
        throw Error.Null
    }
 
    guard y > 0 else {
        throw Error.Negative
    }
     
    guard y != 0 else {
        throw Error.Negative
    }
 
    return Double(x)/Double(y)
}
 
 
do {
    try divide(10, y: nil)
} catch Error.Zero {
    print("Do not do that, man!. Do not pass a zero as second argument!")
} catch Error.Negative {
    print("Do not do that, man!. Do not pass a negative number as second argument!")
} catch Error.Null {
    print("Do not do that, man!. Do not pass any nil argument!")
}

Leave a Reply

Your email address will not be published. Required fields are marked *