Error Handling in Swift 3: Try, Try?, and Try!

Joyce Matos
4 min readNov 9, 2016

--

Some of you may already be familiar with using the keyword try to handle errors in Swift. For those of you who do not know what try is, try is simply a way of dealing with a possible error in any given function. Often times we have functions that “throw”, meaning they have the ability to throw an error, and try is a neat keyword that lets us deal with the possibility of those errors.

When building our apps, there are three variations of try that we can use.

try
try?
try!

If you’re intimidated by these variants, don’t be! And if you’re unsure about which one to use don’t worry, I’ve got you covered.

As I mentioned before, try is normally used in conjunction with a throw function, so we’ll talk a little about throw functions and see our try keywords in action.

Imagine we are trying to parse through some JSON so that we can bring this data into our application. There’s a possibility that this data may not reach our app because the internet may be down, or perhaps the data you are trying to pull from no longer exists! These are all situations that allow for possible errors as we try to retrieve data from our API’s. Swift however, has a built in function in it’s JSONSerialization method that is a throws by default. Swift knows that our attempt to retrieve data may bring some errors and has created a function that allows us to account for this scenario in the event that it does throw us an error.

When using try, our throw function has to be wrapped in a do-catch statement. This allows us to customize our error handling and perform specific actions based on the error.

Below we will use try to gracefully catch any errors.

do {
let responseJSON = try JSONSerialization.jsonObject(with: data, options: []) as! [[String: Any]]
completion(responseJSON) } catch { print(“Hm, something is wrong here. Try connecting to the wifi.“) }

As developers, using try with a do-catch statement allows us to check for meaningful errors, handle them with style, and save our users from being frustrated. Think about it, do we really want our app to crash when our users don’t have any internet connection, or do we want to let them know that this app will continue to run as soon as they connect to wifi? In a scenario like this, a notification is much more helpful and our do-catch statement allows us to do just that.

On the flip-side, we can also handle our errors using try?. If you have any experience with optionals then you’ll soon realize that try? is just that. try? let’s us ignore our error and allow them to become nil whenever our function throws them. For this reason, we do not have to wrap our code in a do-catch statement. We do however, have to unwrap our variable if we are ever assigning a try? value to it.

let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) as! [[String: Any]]if let responseJSON = responseJSON {
print("Yay! We have just unwrapped responseJSON!"
}

The code above assigns an optional value to responseJSON and allows it to become nil if the function called throws any errors. Using try? is helpful when the error that may be thrown is of little importance and we want to avoid assigning any actions to the error that was thrown.

Being that our responseJSON will be an optional we can integrate a guard statement into our code instead and just unwrap it in a readable manner.

guard let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) as! [[String: Any]] else { return }

Lastly we have try!, which operates like a forced-unwrapped optional. This variant basically makes the assertion that although the function has the ability to throw an error, this will never happen because the information we are trying to receive is guaranteed to be there. In other words, try! is used when we know there is no possible way our method will fail, or if this line of code is so fundamental that our whole app might as well crash if there is in fact an error.

let responseJSON = try! JSONSerialization.jsonObject(with: data, options: []) as! [[String: Any]]

Like all values that are forced-unwrapped, try! should be used carefully.

In Conclusion

  • try is used with a do-catch statement and allows for a more detailed approach to error handling.
  • try? ignores our errors and will set them to nil if they happen to occur.
  • try! force unwraps your code and guarantees that our function will never encounter an error. In the case that our function does throw an error our app will simply crash.

--

--

Joyce Matos

iOS Developer // Flatiron School Grad // Life long student fascinated by the unknown