Error Handling

Posted on October 23, 2016


Representing and Throwing Errors

In Swift, errors are represented by values of types that conform to the Error protocol. This empty protocol indicates that a type can be used for error handling.

Error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive.As such, the performance characteristics of a throw statement are comparable to those of a return statement.

There are four ways to handle errors in Swift. You can:

Propagate the error from a function to the code that calls that function

Use a throwing function func canThrowErrors() throws -> String. Any code that calls this method must either handle the errors—using a do-catch statement, try?, or try!—or continue to propagate them.

Handle the error using a do-catch statement

Use a do...try...catch statement:

    do {

        try [expression]

	    [statements]

    } catch [pattern 1] {

        [statements]

    } catch [pattern 2] where [condition] {

        [statements]

    }

The catch clauses don’t have to handle every possible error that the code in its do clause can throw. If none of the catch clauses handle the error, the error propagates to the surrounding scope. However, the error must be handled by some surrounding scope.

Handle the error as an optional value

You use try? to handle an error by converting it to an optional value. If an error is thrown while evaluating the try? expression, the value of the expression is nil: let x = try? someThrowingFunction()

Assert that the error will not occur

Sometimes you know a throwing function or method won’t, in fact, throw an error at runtime. On those occasions, you can write try! before the expression to disable error propagation and wrap the call in a runtime assertion that no error will be thrown. If an error actually is thrown, you’ll get a runtime error.

The Result Type

According to Matt Gallagher’s post:

Examples of scenarios you might want to handle where Swift’s error handling won’t help include:

  • results passed between threads
  • results asynchronously delivered to the current thread
  • results retained for any duration
  • results passed into a function rather than out of a function

The obvious candidate in these other scenarios is a Result type. Where Swift’s error handling encapsulates the composite nature of error handling by using “value” and “error” return paths from a function, a Result type embeds the “value” or “error” directly into a composite data type:

enum Result<Value> {
   case success(Value)
   case failure(Error)
}

Other resources

Error Handling in The Swift 3 Programming Language

Magical Error Handling in Swift

Values and errors, part 1: ‘Result’ in Swift

Meaningful Composite Errors by Ian Keen

Stay in touch!