CloseHomeAboutBlogContactCourse
Bob I’m an iOS instructor/blogger from S.Korea.July 14 • 5 min read • Edit

Intro to Error Handling in Swift

Try? Try! Try: Deal with unexpected results

Story of Mine

When I was younger, I opened up the Swift Documentation. I read each chapter more than once except Error Handling. For some reason, the phrase gave me an impression as if I had to be a professional debugger to fully understand the chapter.

Even then, I feared Error Handling. Those words like catch, try, throw and throws didn’t much make sense to me. They looked scary af. If you’ve never met them before, don’t they sound appalling to you? Don’t you worry, my friend. I’m here for you.☝️

I told my 13-year-old sister that error handling was another way to write an if-else block to send an error message.

Error Message from Tesla

As you may know, Tesla cars have the autopilot feature. But, when the car gets confused for whatever reasons, it asks you to hold the handlebar and indicate an “error” message. In this lesson, you are going to learn how to give that message with Error Handling.

We will create a program which detects objects like traffic lights on the street . Prerequisites are, not limited to, machine learning, vector calculus, linear algebra, probability, and discrete mathematics. Just kidding. I don't even know.

Introducing If-Else Statement

To fully appreciate Error Handling in Swift, we should look into your past. So, this is what many, if not all, new developers would do in order to deal with an error message,

var isInControl = true
func selfDrive() {
 if isInControl {
  print("You good, let me ride this car for ya")
 } else {
  print("Hold the handlebar RIGHT NOW, or you gone die")
 }
}

selfDrive() // "You good..."

Problem

The biggest problem is its readability when the else block becomes bloated. First, it doesn’t indicate whether the function itself contains an error message unless you read through the entire function, unless you name the function like, selfDriveCanCauseError which still kinda works.

Look, the function can kill the driver. You want clearly inform your team members this function is dangerous and even fatal if not handled with care.

The second problem is that what if you execute some complicated functions or actions within the else block? For example,

else {
 print("Hold the handle bar Right now...")

 // If handle not held within 5 seconds, car will shut down
 // Slow down the car
 // More code ...
 // More code ...
}

The else block becomes fat, and it’s like you are trying to shoot a basketball with winter clothes on, which I often do since Korea is pretty cold. You know what I’m sayin? It just ain’t look pretty nor readable.

Well, you could add functions in the else block instead of hard coding.

else {
 slowDownTheCar()
 shutDownTheEngine()
}

However, along with the first problem, there is no explicit way to indicate that that selfDrive() function is dangerous, and should be handled with caution. So, let us dive into Error Handling to write modularized and explicit error messages.

Introducing Error Handling

So far, you are aware of the problem with If-else to handle error messages. The example above was a bit too simple. Instead, let us assume there are two error messages: 1. you are lost 2. the car battery is low.

I’m going to create an enum that conforms to Error protocol.

enum TeslaError: Error {
 case lostGPS
 case lowBattery
}

To be honest, I don’t really know what the Error protocol does, but for Error Handling, you just have to do. It’s like, Why does your laptop turn on when you press the button? Why does your phone unlock when you swipe the screen?

This is how Swift engineers decided to implement, so I’m not going to question their motives. I follow what they’ve made for us. Of course, if you wish to learn, you can download the Swift source code and dissect just like how you open up your laptop or iPhone. I’m just not going to do it.

If you are lost, just bear with me a few more paragraphs. You will see how TeslaError will all come together with a function.

Let’s first send error messages without Error Handling implemented.

var lostGPS: Bool = true
var lowBattery: Bool = false

func autoDriveTesla() {
 if lostGPS {
  print("I'm lost, bruh. Hold me tight")
  // A lot more code
 }

 if lowBattery {
  print("HURRY! 💀")
  // Loads of code
 }
}

So, if I were to run this

autoDriveTesla() // I'm lost, bruh. Hold me tight

But, let’s implement Error Handling. First, you have to indicate the function explicitly that it is dangerous, and can send/throw errors. We insert the keyword, throws next to the function.

func autoDriveTesla() throws { ... }

Now, the function automatically tells your teammate that autoDriveTesla is a different animal without reading through the entire function block.

Sounds good? Okay, it’s time to send/throw those errors when the driver encounters lostGPA or lowBattery within the Else-Ifblock. Remember the TeslaError enum?

func autoDriveTesla() throws {
 if lostGPS {
  throw TeslaError.lostGPS
}
 if lowBattery {
  throw TeslaError.lowBattery
}

Let me catch y’all

If lostGPS is true, the function will send TeslaError.lostGPS. But, what do you do after this? Where do we insert that error message and a lot more code to deal within the else block supposedly?

print("Bruh, I'm lost. Hold me tight")

Okay, I don’t want to overwhelm you, so let’s start off how to run this function with the throws keyword attached.

Since this is a different animal, you have to use run the function by calling try inside of a do block. You are like, “what?”. Just bear with me for a bit.

do {
 try autoDriveTesla()
}

I know what’s in your head, “I really wanna print that error message, if not my driver is gonna die”.

So, where do we insert that error message? Well, we know the function can possibly send two error messages: 1. TeslaError.lowBattery and 2. TeslaError.lostGPS. When the function throws an error, you have to “catch” those error thrown, and once “caught”, you print an error message. You might be a bit confused, so let’s take a look.

var lostGPS: Bool = false
var lowBattery: Bool = true

do {
 try autoDriveTesla()
 } catch TeslaError.lostGPS {
  print("Bruh, I'm lost. Hold me tight")
 } catch TeslaError.lowBattery {
  print("HURRY! 💀")
 }
}

// Results: "HURRY! 💀"

Error Handling with Init

Not only you can apply error handling to functions, but also when you try to initialize an object. Let us say, if you don’t put a name to a course, you will throw an error.

// Define Error Type
enum CourseError: Error {
 case noName
}

// Create Structure
struct UdemyCourse {
 let courseName: String
 init(name: String) throws {
  if name == “” { throw CourseError.noName }
  self.courseName = name
 }
}

// Init & Handle Error
do {
 try UdemyCourse(name: “UIKit Fundamentals with Bob”)
 } catch NameError.noName {
  print(“Bob, you need to enter the name”)
 }

If you enter try UdemyCourse(name: ""), the error message will occur.

When to use Try! and Try?

Good. Try is only used when you run a function/init within the do-catch block. However, if you don’t care to notify your user what’s happening through printing error messages or deal with the error, you don’t technically need the catch block.

who are you, try?

Let’s begin with try? Although not recommended,

let newCourse = try? UdemyCourse("Functional Programming")

try? will always return an optional object. So you have to unwrap newCourse like,

if let newCourse = newCourse { ... }

If the init method throws an error, like,

let myCourse = try? UdemyCourse("") // throw NameError.noName

myCourse will going to be nil.

who are you, try!

Unlike try? it will not return an optional value, but a normal type. For example,

let bobCourse = try! UdemyCourse("Practical POP")

bobCourse is non-optional. However, if the init method throws an error like,

let noCourseName = try! UdemyCourse("") // throw NameError.noName

It’s going to crash. Just like force unwrapping with !, never use it unless you have to by default or you are 101% sure what’s going on.

That’s it. You’ve fully understood the concept of Error Handling with me in this tutorial. Nice and easy. No need to become a professional debugger.

Last Remarks

I hope you’ve learned something new with me. If you’ve found this implementation useful, sharing would be pretty nice of you. If you wish to learn more about advanced error handling with rethrows and other intermediate Swift topics such as memory management, advanced enums, and more, feel free to join [Learn Swift with Bob](Learn Swift with Bob).

About Me

iOS Developer from South Korea. Feel free to follow my story on Instagram or get serious on LinkedIn