try, try! & try? what’s the difference, and when to use each? - swift

In Swift 2.0, Apple introduced a new way to handle errors (do-try-catch).
And few days ago in Beta 6 an even newer keyword was introduced (try?).
Also, knew that I can use try!.
What's the difference between the 3 keywords, 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

Related

Single try meaning in Swift

I looked for this article, but it does not cover my case.
If i understand correct, we can use try either in do..catch.. statement or in a function that can throw.
But sometimes i see something like:
let jsonData = try jsonEncoder.encode(employee1)
Where jsonData is not optional. What is meaning of this? What if try sttement fail? Why value is not optional? Can someone explain? Thanks.
In addition to the cases you mentioned, you can call try at
top-level code. Here is a simple self-contained example:
// main.swift:
enum MyError : Error {
case failed
}
func foo() throws -> Int {
throw MyError.failed
}
defer { print("Good bye.") }
let x = try foo()
print(x)
You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:
$ swiftc main.swift
$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4
The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.
This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).
The error message originates from ErrorType.swift, line 187:
/// Invoked by the compiler when code at top level throws an uncaught error.
#_inlineable // FIXME(sil-serialize-all)
#_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift).
Apparently the top-level code behaves as if embedded in a
do-catch block:
do {
func foo() throws -> Int {
throw NSError(domain: "foo", code: 0, userInfo: nil)
}
defer { print("Good bye.") }
let x = try foo()
} catch {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
A function call marked with try like the one you posted, as you stated, must be located inside a function marked with the keyword throws or a do/catch statement or it will cause a compile error.
On a side note if you want to call a throwing function outside a do/catch or another throwing function you can use try? before your function call to get an optional result that will be nil if something is thrown. There is also the try! variant that assumes nothing will be thrown returning a non-optional result, but if something is thrown the app will crash.

Swift: Error ignores try statement

In my code I'm deserialising some XML using the SWXMLHash library. Its .value() method has the throws keyword in its declaration, as do any custom deserialise functions.
I have the following line of code:
let myValue : UInt8 = try? xml["Root"]["ValueNode"].value()
Since the library doesn't include a deserialiser for UInt8, I've defined my own:
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
return UInt8(element.text)!
}
}
This works when the node has a value. However, when the node doesn't exist or is nil, an error occurs on the following line:
return UInt8(element.text)! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
This is supposed to happen, obviously. What I don't understand is why this error is not being caught by my try? statement and returning nil instead of throwing that error.
Can anyone help?
Not all errors can be caught in Swift. If you mark a function using the throws keyword it indicates that the function might throw a recoverable error. However, your custom implementation doesn't actually throw any errors. Only errors thrown from functions marked with the throws keyword and thrown using the code throw Error can be caught by a do-catch block.
try? is a way to convert a throwable function's return value to an optional. If the function would throw an error, the value after the try? will be nil, otherwise it will be an optional value.
When you use the !, you specifically tell the compiler that you know what you are doing and if the operation on which you used the ! fails, your app shouldn't fail gracefully.
You'll need to change your deserialize method to handle the optional unwrapping gracefully or throw and error.
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
if let val = UInt8(element.text) {
return val
} else {
throw NSError(domain: "Couldn't deserialize value to UInt8", code: 1)
}
}
}
return UInt8(element.text)!
There's no try in this line. Therefore, no errors are going to be thrown here. If UInt8 can't convert the string it's given, it just returns nil. And then, of course, your ! turns that nil into a crash.
Instead of that, do something like this instead:
guard let retVal = UInt8(element.text) else { throw SomeError }
return retVal
In general: When there's a way to do something using !, and another way to do the same thing without using !, go for the second one unless you've got a really good reason.

using guard to check for nil without implicitly unwrapping

I know there are some similar questions around, but I couldn't find one specific to my issue.
I have a request where I want to check for the presence of the error key. it is not present everything is fine, if not I should handle the error. Currently, I have it implemented as follows:
if let error = json["error"] {
// handle error
}
else {
// handle success
}
I would like to use a guard statement here to have the success case unindented. The only way I came up with is
guard json["error"] == nil else {
let error = json["error"]!
// handle error
}
// handle success
but that seems wrong to me with the !. Are there any other approaches to this?
In your guard code you would have to have a return statement in the else block. Like this...
guard json["error"] == nil else {
let error = json["error"]!
// handle error
return
}
// handle success
But you are correct. Having to force unwrap the error is not ideal.
So in this case. I think guard is the wrong solution. Instead use if but return from the conditional block. This removes the need for using an else block.
if let error = json["error"] {
print(error)
// handle error
return
}
// handle success...
// no need for else block. Just return from the if in the error case.
The difference between guard let and if let is where the unwrapped optional is scoped.
With guard it is scoped outside the block with if it is scoped inside the block.
An idea for your issue was proposed on the Swift Evolution mailing list:
"guard not let" optional binding
https://forums.swift.org/t/idea-guard-not-let-optional-binding/2614
[it is] fairly common that you want to check that an optional is nil, and still bail if it isn’t (maybe using the value that you now know exists), e.g:
guard cachedValue == nil else { return cachedValue! }
cachedValue = //… expensive calculation
It seems a little bit “unfair” that we have this lovely clean let syntax when checking for Optional.Some, but we to have to do this ugly manual check against nil and explicit unwrap when checking for Optional.None. There is literally no other way to satisfy the guard statement; our optional bindings only go one-way can’t be evaluated.
Unfortunately that construct does not currently exist in Swift.
The alternatives are the slightly awkward/duplicated guard syntax, possibly with a force-unwrap:
guard json["error"] == nil else {
return json["error"]!
}
or using if-let (which does not enforce scope-exit like guard):
if let error = json["error"] {
return error
}

How to avoid returning an optional after throwing an exception?

I'm writing a utility function which takes a parameter and always returns a valid non-nil result (notice the returned value is not optional because the possible parameters are all actually hardcoded and valid, so I know the function cannot fail):
func myFunction(param: String) -> NonTrivialObject {...}
Now, during development I want to experiment with possible parameters and, should I make a mistake, I want the function to throw an exception and just crash. I don't want or need to throw Swift errors or catch them, I want to hard-crash and fix the parameter immediately. In Objective C I would just use NSParameterAssert() or do something along these lines:
guard let validatedParam = param where param != nil else {
NSException(...).raise()
return nil
}
// do the actual work and return a non-optional result
However, I cannot return nil because the result is not an optional. Is there a way to somehow tell the compiler that it doesn't need to bother returning anything from the function after an exception is thrown? Or am I doomed to litter my code with unwrapping optionals or try! statements or to return a dummy object just to make the compiler pleased?
You can use Swift assert(_:_file:line:) function as follows
assert(some condition, "Message to display if condition is false (optional)" )
If the condition is verified the app will continue running, otherwise it will terminate
An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.
You can define the function to be a throwing function:
func foo(param: String) throws -> NonTrivialObject {
guard param != nil else {
throw SomeErrorEnum.NilFound
}
doStuff(...)
}
The error enum needs to conform to the ErrorType protocol. The function call is now able to catch errors like this:
do {
try foo(param)
} catch SomeErrorEnum.NilFound {
print("Found nil")
}
In this way the function returns a non-optional but can also throw errors. For more information see: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html

Errors in Swift initialisers

What is the best way to handle an init that might fail in Swift? For example, you create an instance of class that depends on a certain resource that might not be available.
Apparently we have 2 options:
A bailable init that returns nil (the Cocoa way)
An init that throws an error
See below
enum ThingError: ErrorType{
case crap
}
class Thing {
init(c: Int) throws{
if c < 0 {
throw ThingError.crap
}
}
}
var c = try Thing(c: 3)
do{
var d = try Thing(c: -4)
}catch{
print("oh vey!")
}
Is there a recommended way of doing this? The second option seems more "Swifty"...
Neither is inherently better or Swiftier.
Personally I find throws initializers a huge pain. I'd much rather have a failed initializer return nil, because then I can do my initialization with guard let instead of having to wrap things in do/catch and deal with the resulting scoping issues. Your code illustrates the problem; your var d is "stuck" inside a do scope. I'd rather say this:
guard let d = Thing(c:-4) else {return}
// now d is unwrapped and in scope!
...than this (what you have to say):
do {
var d = try Thing(c: -4)
} catch {
print("oh vey!")
}
// and here there is no `d`, so _now_ what?
On the other hand, throwing an error offers an opportunity to send a message, i.e. to be communicative about exactly what went wrong. You can't do that with a mere init? initializer; it works or it fails, and that's all the caller knows.