Exceptions and Error Handling

As of version 8.2, Silver supports and embraces the new error handling syntax that Apple introduced with Swift 2.0 at WWDC2015, and extends it to cover exception handling, as well. The previous temporary exception handling syntax using __try, __finally and __catch has been deprecated.

Background

In Swift 2.0 and Silver 8.2 and beyond, error handling is done with the do/catch keyword combination, and cleanup code (the traditional finally block of exception handling) can be performed with an independent defer statement.

do/catch work pretty much as exception handling works in the other languages – the code inside the do scope is protected against failures, and one or more catch clauses can be provided where execution will continue if an exception or error occurs. Separate catch clauses can be defined to catch different kinds of errors, by class, or by more complex pattern.

defer blocks can be used to specify code that will run at the end of the current scope, regardless of whether an error or exception occurred, or whether the method exited prematurely via a simple return. This makes defer statements incredibly useful even when error handling is not involved.

.NET and Java

On .NET and Java, Silver simply uses the do/catch syntax to protect code against exceptions, and executes the closest matching catch block when an exception occurs. Keywords aside, this is no different than regular try/catch or try/except blocks in C# or Oxygene. If no variable is provided for the catch block, a default error variable will be in scope, containing the current exception. It can be re-thrown with the throw keyword.

The try (and try!) keyword is not used or necessary for exception handling on .NET and Java. It can be specified and will be ignored. The try? syntax is fully supported, and will catch any exception and return a nullable.

do {
    Int32.Parse("Five");
} catch {
    println("could not parse string, failed with \(error)")
}

Cocoa

On Cocoa, the do/catch syntax combines handling of both errors (a concept which does not translate to .NET or Java) and exceptions in the following ways:

  • Just like on .NET and Java, a do/catch block protects against exceptions, and executes the corresponding catch clause if one occurs.

  • In addition, the try keyword can be used inside the do scope to call methods that follow Cocoa's and Swift's pattern of returning an NSError value.

When called with the try keyword, these methods drop the last NSError parameter, and their result is converted to be non-nullable (for functions that return a nullable value) or to be void (for functions that return a Boolean). When the function call returns nil or false, the remainder of the do scope will be skipped, and execution will continue with the closest matching catch clause for the received NSError. No actual exception will be raised.

When called without the try keyword, these methods can be called the "old fashioned" way, and handling the return value and any returned error value will be up to the calling code. They will not participate in any error handling logic provided by do/catch.

Note: The try keyword is not used or necessary for handling exceptions, but only for handling errors.

do {
    try NSFileManager.defaultManager.contentsOfDirectoryAtPath(path)
} catch {
    println("could not read directory, failed with \(error)")
}

Converting Errors to Exceptions

Silver also allows you to use the try! and try? keywords (with exclamation point or question mark, respectively) to make calls to methods that follow the NSError pattern, without a do/catch clause. If the method containing the try! statement itself follows the NSError pattern, any error received will be passed on to the caller of the current method. If the method does not follow the pattern, any error will be wrapped in an Exception and thrown up the call stack. try? will catch errors and cnvert them into a nil result.

func countFilesInDirectory() -> Int throws {
    let files = try! NSFileManager.defaultManager.contentsOfDirectoryAtPath(path)
    return files.count
}

The NSError Pattern

The above discussion of try and try! statements applies only to calls to methods that follow a certain declaration pattern. These methods must qualify for one of the following three scenarios:

  • They must return a nullable result value and an optional __out NSError value as last parameter.
  • They must return a boolean result value and an optional __out NSError value as last parameter.
  • They must be defined in Swift using the throws keyword.

Throwing Errors

The throw keyword can be used to throw an exception (all platforms) or an NSError (on Cocoa, inside methods adorned with the throws keyword).