try, try! & try? what’s the difference, and when to use each?
Updated for Swift 5.1
Assume the following throwing function:
enum ThrowableError: Error { case badError(howBad: Int)}func doSomething(everythingIsFine: Bool = false) throws -> String { if everythingIsFine { return "Everything is ok" } else { throw ThrowableError.badError(howBad: 4) }}
try
You have 2 options when you try calling a function that may throw.
You can take responsibility of handling errors by surrounding your call within a do-catch block:
do { let result = try doSomething()}catch ThrowableError.badError(let howBad) { // Here you know about the error // Feel free to handle or to re-throw // 1. Handle print("Bad Error (How Bad Level: \(howBad)") // 2. Re-throw throw ThrowableError.badError(howBad: howBad)}
Or just try calling the function, and pass the error along to the next caller in the call chain:
func doSomeOtherThing() throws -> Void { // Not within a do-catch block. // Any errors will be re-thrown to callers. let result = try doSomething()}
try!
What happens when you try to access an implicitly unwrapped optional with a nil inside it? Yes, true, the app will CRASH!Same goes with try! it basically ignores the error chain, and declares a “do or die” situation. If the called function didn’t throw any errors, everything goes fine. But if it failed and threw an error, your application will simply crash.
let result = try! doSomething() // if an error was thrown, CRASH!
try?
A new keyword that was introduced in Xcode 7 beta 6. It returns an optional that unwraps successful values, and catches error by returning nil.
if let result = try? doSomething() { // doSomething succeeded, and result is unwrapped.} else { // Ouch, doSomething() threw an error.}
Or we can use guard:
guard let result = try? doSomething() else { // Ouch, doSomething() threw an error.}// doSomething succeeded, and result is unwrapped.
One final note here, by using try?
note that you’re discarding the error that took place, as it’s translated to a nil.Use try? when you’re focusing more on successes and failure, not on why things failed.
Using Coalescing Operator ??
You can use the coalescing operator ?? with try? to provide a default value incase of failure:
let result = (try? doSomething()) ?? "Default Value"print(result) // Default Value