Magical Error Handling in Swift

In this tutorial you will learn all about error handling in Swift. You’ll learn about all the new features added in Swift 2.0 and discover how to use them. By Gemma Barlow.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Propagating Errors

throws

The throws keyword is required in Swift if a function or method throws an error. Thrown errors are automatically propagated up the call stack, but letting errors bubble too far from their source is considered bad practice. Significant propagation of errors throughout a codebase increases the likelihood errors will escape appropriate handling, so throws is a mandate to ensure propagation is documented in code – and remains evident to the coder.

rethrows

All examples you’ve seen so far have used throws, but what about its counterpart rethrows ?

rethrows tells the compiler that this function will only throw an error when its function parameter throws an error. A quick and magical example can be found below (no need to add this to the playground):

func doSomethingMagical(magicalOperation: () throws -> MagicalResult) rethrows -> MagicalResult {
  return try magicalOperation()
}

Here doSomethingMagical(_:) will only throw an error if the magicalOperation provided to the function throws one. If it succeeds, it returns a MagicalResult instead.

Manipulating Error Handling Behavior

defer

Although auto-propagation will serve you well in most cases, there are situations where you might want to manipulate the behavior of your application as an error travels up the call stack.

The defer statement is a mechanism that permits a ‘cleanup’ action to be performed whenever the current scope is exited, such as when a method or function returns. It’s useful for managing resources that need to be tidied up whether or not the action was successful, and so becomes especially useful in an error handling context.

To see this in action, add the following method to the Witch structure:

func speak() {
  defer {
    print("*cackles*")
  }
  print("Hello my pretties.")
}

Add the following code to the bottom of the playground:

func exampleThree() {
  print("") // Add an empty line in the debug area

  let witchThree = Witch(name: "Hermione", familiar: nil, hat: nil)
  witchThree.speak()
}

exampleThree()

In the debug console, you should see the witch cackle after everything she says.

Interestingly, defer statements are executed in the opposite order to which they are written.

Add a second defer statement to speak() so that a Witch screeches, then cackles after everything she says:

func speak() {
  defer {
    print("*cackles*")
  }

  defer {
    print("*screeches*")
  }

  print("Hello my pretties.")
}

Did the statements print in the order you expected? Ah, the magic of defer!

More Fun with Errors

The inclusion of the above statements in Swift brings the language into line with many other popular languages and separates Swift from the NSError-based approach found in Objective-C. Objective-C errors are, for the most part, directly translated, and the static analyzer in the compiler is excellent for helping you with which errors you need to catch, and when.

Although the do-catch and related features have significant overhead in other languages, in Swift they are treated like any other statement. This ensures they remain efficient – and effective.

But just because you can create custom errors and throw them around, doesn’t necessarily mean that you should. You really should develop guidelines regarding when to throw and catch errors for each project you undertake. I’d recommend the following:

  • Ensure error types are clearly named across your codebase.
  • Use optionals where a single error state exists.
  • Use custom errors where more than one error state exists.
  • Don’t allow an error to propagate too far from its source.

The Future of Error Handling in Swift

A couple of ideas for advanced error handling are floating around various Swift forums. One of the most-discussed concepts is untyped propagation.

– from Swift 2.x Error Handling

“…we believe that we can extend our current model to support untyped propagation for universal errors. Doing this well, and in particular doing it without completely sacrificing code size and performance, will take a significant amount of planning and insight.”

– from Swift 2.x Error Handling

Whether you enjoy the idea of a major error handling change in a future version of Swift, or are happy with where things are today, it’s nice to know that clean error handling is being actively discussed and improved as the language develops.

Excellent

Where To Go From Here?

You can download the finished set of playgrounds here for this tutorial.

For further reading, I recommend the following articles, some of which have already been referenced throughout this tutorial:

If you’re keen to see what may lie ahead in Swift, I recommend reading through the currently open proposals; see Swift Language Proposals for further details. If you’re feeling adventurous, why not submit your own? :]

Hopefully by now that you’ve been truly enchanted by error handling in Swift. If you have any questions or comments on this tutorial, please join the forum discussion below!

Gemma Barlow

Contributors

Gemma Barlow

Author

Over 300 content creators. Join our team.