There’s many ways to handle errors in Swift. For some errors, using throws
is a great way to handle errors without crashing the system.
Download the exercise file. You’ll find a project with an embedded playground.
let coffees = ["Sumatra","Colombia","Dark Energy","Sarabanda Dark","Kona"] let ratings = [2,2,-1,5] func coffee(_ name:String) -> String{ let index = coffees.firstIndex{ (coffee) -> Bool in coffee == name } let ratingIndex = index! let rating = ratings[ratingIndex] return String(repeating:"☕️", count: rating) } var myCoffee = "Colombia" print(myCoffee + ":" + coffee(myCoffee))
While there’s a lot better ways to do this, I’ll use an example of a function coffee(name:)
that finds the rating of a type of coffee using the two arrays coffees
and ratings
. You’ll notice there’s a nil error and some index out of range errors possible here in the rating and the search.
I can run for Sumatra as it is set for and it gives me a two cup rating. Try Kona and it crashes since I have four instead of five ratings, so I have an array out fo bounds error. It also crashes on Java, whihc is not one of my coffees, so index
is nil and can’t be force unwrapped.
For internal fatal errors of a function, we can use throws
. To use throws
, add it to the function name before the return type
func coffee(_ name:String) throws -> String{
You’ll need to identify the error when you throw it. You use an enum
that adopts the Error
protocol to do that. I’ll add three errors
enum CoffeeError: Error{
I’ll add three errors. The first case will be a coffee not found
case coffeeNotFound
For the second, I can add an argument, which I can later use to give more information about the error in my code
case ratingNotFound(coffee:String)
The last will be for an invalid rating, since I will only use ratings between one and five cups.
case badRating }
Going back to my code, I’ll throw those errors in my method. The first error is an nil
index, which I will use a guard
for.Inside the guard’s else statement I’ll throw the error and send the enumeration’s value, in this case coffee not found.
//let ratingIndex = index! guard let ratingIndex = index else{ throw CoffeeError.coffeeNotFound }
For my other two errors, where the rating index is out of range, I’ll use an if statement and throw the appropriate error. For the rating not found, I’ll.include in the parameter the name of the coffee
if ratingIndex >= ratings.count{ throw CoffeeError.ratingNotFound(coffee: name) }
If my rating is a nagative one, I’ll throw an error
if rating < 0 { throw CoffeeError.badRating }
If you make your own thrown errors, that’s how you would set them up. This is great way to detect errors you’ll get from situation beyond your control.
More likely, you will be handling the error. You might set up your own, but there are a lot of factory methods that throw errors. The standard way of handling thrown errors is the do…try…catch
construct. It starts with a do
and a code block
do{ print(myCoffee + ":" + coffee(myCoffee)) }
Inside the code block, you try
the thrown
method, usually before the method. In a print
method, it goes outside the print
.
do{ try print(myCoffee + ":" + coffee(myCoffee)) }
Add catch
to handle any error. In its simplest form, you can do this to catch and handle all errors.
do{ try print(myCoffee + ":" + coffee(myCoffee)) } catch { print("Error") }
The real power of do…try…catch however is in specifying the error form your specified errors, so you can handle it properly. I’ll set up a specific error handler for a coffee not found. I’ll place this above the gnereal catch. You can think of catch a lot like switch, where the catch without any errors is the default, and should go last.
catch CoffeeError.coffeeNotFound{ print("Coffee not found") }
The ratingNotFound
error had a parameter, I can set the error parameter with a let in the parameter to silence a warning from string interpolation.
catch CoffeeError.ratingNotFound(let coffee){ print ("Coffee \(coffee) does not have a rating" ) }
Try a few different cases of errors. Set the coffee to Dark Energy, run, and you get a simple error from the -1 rating

Change the name to Dark Matter, which isn’t one of our coffees

Try Kona for the out of bounds error:

If yo dont need to hadle errors differently, an alternative is making the error a nil
value using try?
and use an if Let
construct.
if let coffeeRating = try? coffee(myCoffee){ print (myCoffee + " " + coffeeRating ) } else { print("Error on coffee") }
There’s one other option: Disable the error thrown with try!. Use this only if you know this will work, such as
myCoffee = "Sumatra" try! print(myCoffee + ":" + coffee(myCoffee))
The best way to handle an error is not to make one. However, when creating methods and classes, you will find places that there are errors you can’t control. Throwing errors will notify any other classes and methods that there is trouble.
The Whole Code
You can find the completed project below, You can also download it from GitHub.
// // A Demo for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit let coffees = ["Sumatra","Colombia","Dark Energy","Sarabanda Dark","Kona"] let ratings = [2,2,-1,5] enum CoffeeError: Error{ case coffeeNotFound case ratingNotFound(coffee:String) case badRating } func coffee(_ name:String) throws -> String{ let index = coffees.firstIndex{ (coffee) -> Bool in coffee == name } //let ratingIndex = index! guard let ratingIndex = index else{ throw CoffeeError.coffeeNotFound } if ratingIndex >= ratings.count{ throw CoffeeError.ratingNotFound(coffee: name) } let rating = ratings[ratingIndex] if rating < 0 { throw CoffeeError.badRating } return String(repeating:"☕️", count: rating) } var myCoffee = "Sumatra" do{ try print(myCoffee + ":" + coffee(myCoffee)) } catch CoffeeError.coffeeNotFound{ print("Coffee not found") } catch CoffeeError.ratingNotFound(let coffee){ print ("Coffee \(coffee) does not have a rating" ) } catch { print("Error") } if let coffeeRating = try? coffee(myCoffee){ print (myCoffee + " " + coffeeRating ) } else { print("Error on coffee") } myCoffee = "Sumatra" try! print(myCoffee + ":" + coffee(myCoffee))
Leave a Reply