Lesson 016 – Error Handling Basics Part 1

ErrorIn our last lesson, we glossed over handling the errors that could occur during our JSON parsing because I wanted to focus on the specific functionality we were tackling. Well, it is time to pay the piper, now! Let’s take a look at how we handle errors in Swift.

Throwing Errors the Unsafe Way

Let’s pretend that we are going to be good little stewards of our methods and we are going to check our parameters against boundary issues. And, if the value passed in is outside of our expected range, we will throw an exception. Given the following method, we want to add the parameter check to make sure that the parameter is not zero.

One thing we can do is use the fatalError method that Swift provides us, like this

Throwing Errors the Safe Way

If I call our method with a 0 value, an error is raised that says fatal error: Parameter ‘theNumber’ cannot be zero. That is one way to handle it, but it is not the preferred, “Swift” way. That isn’t very safe. Users of our method would have no idea that an exception could be thrown. However, we can let users know and force them to deal with it. Our first step is to declare our own error type and have it inherit from Swift’s ErrorType. I’m going to elect to create a MathError type and add an option for DivideByZero.

So, does this mean that we can then just throw this error instead?

No! This code gets us the following compile error: “error: error is not handled because the enclosing function is not declared ‘throws'”. What does that mean? Well, as part of our method declaration, we need to let Swift (and our users) know that we plan to throw an error. All you have to do is add a throws keyword in before the return type. This is how our method will look when it is properly set up.

Now if we try to call this function normally, we get a compile-time error.

When you use Swift Error Handling like it is intended, everything is very intentional, but you can short circuit things. That is what we did in our last lesson. If I include my try so the code will compile, but I am too lazy or don’t care about my users and don’t want to handle the error, I can use try!. So, if I want to call our method but not handle the error, I can write code like this

The second statement executes with the error “fatal error: ‘try!’ expression unexpectedly raised an error: MathError.DivideByZero”. So, if we use try!, we might as well have just raised a fatalError(), because execution will still stop. However, if you want to handle that error, you can just use regular try wrapped in a do…catch block. That kind of good stewardship looks like this

So, unlike some other languages where the block itself is a try…catch, in Swift we attempt a try inside of a do…catch block. That requires just a little bit of a mind shift.

Preparing for Multiple Possible Error Types

Sometimes, more than one kind of thing can go wrong. I’m going to change our MathError type to now look like this

Now, I’m also going to change our method to possibly return another error type. Since our method is dividing 5 by a number and returning an integer, we can’t handle non-whole number return values. Therefore, if someone passes in anything larger than 5, we will throw a MathError.TooLarge error. Here is how that change to the method should look

Now, we can have specific catch blocks to handle each kind of error. We can just pile those catch blocks on top of one another.

Now, when we pass in 6, we output the message “You must provide a number that is less than or equal to five”. If we pass in zero, we output the message “You cannot pass in a zero”. Neither one stops the execution of the entire program. However, it will stop executing what is inside the do block and jump to the appropriate catch, so you should keep your do…catch blocks as specific as possible. Don’t just wrap large portions of code inside of a do block so you do as little error handling as possible.

Conclusion

Error handling is Swift is a bit of a different mindset than some other languages. However, if you are always very explicit and deliberate with your code, you won’t have as many issues. Next time, we’ll take a look at how we should have handled those network calls in our last lesson as well as examining how Swift’s guard keyword works.

Add Comment

Required fields are marked *. Your email address will not be published.