Error Handling in Swift

April 12, 2016

Last year at WWDC Apple introduced a new syntax for handling errors. Lately I have noticed that sometimes I still get a bit confused about how this works so I figured it would be nice to learn more about this subject by writing a blog post.

Type of errors

When an iOS or OSX application is running two different types of problems can arise, exceptions and errors. Exceptions are bugs in our program that warn us that an unexpected condition occurred and needs to be fixed e.g. when your program unexpectedly finds nil when unwrapping an optional value. Exceptions cause applications to crash so we want to avoid having these in our production code at all costs.

Errors are expected problems at the user-level like e.g. trying to load a file that doesn’t exist. Because they are expected during the normal execution of a program we should check for these kind of conditions and inform the user when they occur.

How it was done in the past

In the Objective-C days error handling was done through a mechanism called pass by reference. When we would call a method that could fail we would pass in an indirect reference to an NSError object as a parameter, and if the method would fail this reference would get populated with an NSError containing the error details. In the early days of Swift this was achieved by passing an optional instance of NSError to an NSErrorPointer parameter.

In those times it was easy to forget to check the error, or if you didn’t care about why a method failed you could just pass NULL or nil for the error argument and the error would be ignored. Since one of the core values of Swift is to make it easy to write safe code by default a new error handling syntax was introduced. This syntax makes it clear when we can expect an error and makes sure we handle those errors.

do, try and catch

If we want a method to throw an error we need to create a list of all the possible errors that can occur when executing this method. We do this by creating an enum that represents our type of error. This enum must conform to the ErrorType protocol.

The next step is to write the method that might fail. Here we can use the guard statement to check for errors. If the condition is not met we throw an error.

// Step 2: Write a method that throwsfuncsaveTitle(title:String)throws{// Check if title is validguardtitle.characters.count>0else{throwTitleError.Empty}guardtitle.characters.count>=8else{throwTitleError.Short}guardtitle.characters.count<=40else{throwTitleError.Long}// Save the title}

Now that we created an error and a method that can fail it is time to call this method. When we call a method that can fail we have to precede it with the try keyword, otherwise our code won’t compile. The try keyword makes it instantly clear to the readers of our code that this method can fail.

trysaveTitle(someTitle)

If you are certain that an error will never happen you can use try!. This communicates that you are aware there is a theoretical possibility of this call failing, but you are certain this is not going to happen in your case. If the method does fail your app will crash.

try!saveTitle(someTitle)

Ok nice, now we made sure that everyone who reads our code will know that this method can fail, but we are still not handling the error and therefore our code still won’t compile. For this we use the do-catch statement. We wrap the function that throws in the do block and then catch errors with catch blocks.

do{trysaveTitle(someTitle)// Do some other stuff// If saveTitle fails this code will not be executed}catchTitleError.Empty{print("Error: Save failed because the title is empty")}catchTitleError.Short{print("Error: Save failed because the title is too short")}catchTitleError.Long{print("Error: Save failed because the title is too long")}catch{print("Error: Save failed because of an unknown reason")}

Swift doesn’t know which specific error our method might throw so to make the statement exhaustive we provide a catch all block. This makes our code safer because now we made sure we will catch all possible errors. Now when we run our program and our method fails it will immediately skip to the catch blocks and handle the error.

Sometimes we are not interested in the details of the error but we just want to know if the call failed or succeeded. For this case we have the try? keyword. If we use try? the error is handled by returning an optional that doesn’t contain a value. Therefore there is no need to wrap the method that throws in a do-catch statement. This can be used in combination with optional binding.

So that’s how error handling is done in Swift 2. If you would like to see a more extensive example I advise you too watch this WWDC talk about new features in Swift 2.0.