In this article we will look at how errors are handled in Swift 2
in such a way that your programs will exit gracefully instead of crashing. If we want to throw an error, the object thrown must conform to the ErrorType protocol. Enums
are appropriate for classifying errors, so let’s create one that provides a way of keeping track of all possible errors we could get. Let’s assume this time we can only have two types of errors:
Let’s also create a struct named User that holds a person’s name. If we believe our struct might fail when being instantiated, we can use a failable initializer to ensure that the struct returns nil
instead crashing the app.
Inside the init(:) method we use a guard statement to make sure both the firstname and lastname exist so that the initialization is either successful, or otherwise it fails gracefully.
A function that can throw an error, or calls a function that can throw an error has to be marked with throws. After your program throws an error, you will need to handle that error. Let’s create a function parseData(:) that returns a User struct if the provided argument is valid, or otherwise throws
the first of our defined errors, the InvalidData type:
When calling an error-throwing function, we must embed it in a do block. Within the block, we need to try the function and if it fails, we need to catch it and preferably report an error. When we get to the function that handles the errors, we don’t need to include throws
in the declaration. Let’s create another function named testParsing(:) that calls our error-throwing parseData(:)
function:
If the function doesn’t throw an error, we simply return the user’s name. If it throws an error, however, we need to catch it and report the appropriate error message. You notice we have a second, generic catch
statement which is required so that we have an exhaustive
handling of the error throwing. Next, let’s see how this function works with various arguments:
In the second call, we omitted the firstname
in purpose to see how output looks like:
As expected, the output for the first call is correct, while the output for the second call reports the error we handled in the testParsing(:)
function.
We can also chain multiple throwing function calls together knowing that if any link in the chain fails, the rest of the calls will not execute. Let’s create a method named testConnection(:) that throws the second error we defined in the beginning, the NoConnection type:
It’s just a dummy function that simulates checking a network connection, and which throws an error when the http return code
is different than 200. To test it, let’s also create another function named testThrowing(::) that calls both the throwing functions we created so far:
You will notice that we used the defer keyword which allows the closure that follows it to execute only when the function ends. The advantage of using defer
is that the code will execute when the function throws too, so this is a great place to put code that always needs to run, even when errors occur!
Next, let’s see how this function works with various arguments:
We first provide correct input for both arguments, and then we provide a bad return code
that should lead to a failure, even though the first argument is still correct:
As you expect, the first call returns success while the second reports an error and halts the execution of the entire chain of throwing function calls. This is a powerful feature that we can use whenever we want our program to stop running when there is at least one problem in a long decisional chain. The source code is posted on Github
, as usual.
Until next time!