Swift 2.0 has a new way of error handling. It uses a
do-try-catch syntax, which is the replacement for
NSError . In this post we will discuss how to use this new syntax.
Defining an error
Before an error can be thrown or catched, it must be defined. You can define an error in Swift by an
enum that implements the new
ErrorType protocol:
|
enum MyErrorType : ErrorType { case SomeErrorCause case AnotherErrorCause } |
Throwing an error
For a function to be able to throw an error, it must announce this in the function head by the keyword
throws :
|
func testFunction() throws { } |
Now this function can throw an error by using the keyword
throw and the mention of the concrete error type:
|
func testFunction() throws { throw MyErrorType.SomeErrorCause } |
Catching an error
If you just try to call this function, there will be a compiler error. Because the function announces that it is capable to throw an error, you will need to catch the potential error:
|
do { try testFunction() } catch { print("there was an error...") } |
By the way: Because the
do-try-catch syntax is similar to the
do-while syntax, the latter is renamed to
repeat-until.
Furthermore, in a
do-try-catch block you have the possibility to catch several errors:
|
do { try testFunction() } catch MyErrorType.SomeErrorCause { print("there was an MyErrorType.SomeErrorCause-error...") } catch MyErrorType.AnotherErrorCause { print("there was an MyErrorType.AnotherErrorCause-error...") } catch { print("there was another error...") } |
However, similar to
switch, a
do-try-catch block must be exhaustive. That means that all possible errors must be catched. Since the error throwing function does not announce what kind of errors it can throw, this means that all known errors must be catched. In practice this means that there need to be always a pure catch case.
Cleaning Up
If a function throws an error, then it will return immediately. But because sometimes things needs to be done before a functions returns, there is the new keyword
defer. For example, this clean-up could be closing a file. With defer you can define a block of code that is always executed when a functions returns, no matter whether the function returns normally or due to an error.
You can define your
defer block anywhere in your function. Furthermore, it is also possible to define more than one
defer block. If you do so, all of these blocks will be executed in reverse order.
Take a look at an example:
|
func testFunction() throws { defer { print("clean-up") } throw MyErrorType.SomeErrorCause } |
For more details, take a look at another blog post about this topic.
Error handling interoperability between Objective-C and Swift
Objectice-C is now 100% compatible to Swift’s new error handling. If you use
NSError in the right way, the functions’s head is automatically translated to the correct Swift syntax. Let’s look at an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#import "ObjectiveCTestClass.h" @implementation ObjectiveCTestClass - (BOOL) doSomethingRiskyWithAString:(NSString*) aString error:(NSError**)error { if (aString == nil) { NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; [errorDetail setValue:@"Failed" forKey:NSLocalizedDescriptionKey]; *error = [NSError errorWithDomain:@"aDomain" code:100 userInfo:errorDetail]; return NO; } return YES; } @end |
We have a test class with a function that satisfies two requirements:
- The function has as last parameter a pointer of type
NSError** .
- The function returns something – that means the return type is not
nil .
Now if you declare the ObjectiveCTestClass.h in the BridgingHeader file, the function head is translated to
|
doSomethinhRiskyWithAString(aString: String!) throws -> Bool |
That means, you can call the Objective-C function from Swift in the following way:
|
let objectiveCTestObject = ObjectiveCTestClass() do { try objectiveCTestObject.doSomethingRiskyWithAString(nil) } catch let error as NSError { print("Error: (error.domain)") } |
Note how you can get access to the
NSError pointer in the catch block.
The other way round, if you call a Swift function that throws an error from Objective-C, the function is translated to the
NSError syntax.
Conclusion
Swift’s new error handling model is a very important new feature. It is more intuitiv than the old
NSError way but also very powerful. Like always, the interoperability between Swift and Objective-C is very good. You should be able to adapt very quickly.
References