Better error unwrapping with guard

Often I'm faced with writing this code:

```

func handleError(error: Error) {}

var error: Error?

guard error == nil else {

handleError(error: error!)

return

}

```

which has an implicitly unwrapped optional and the `guard` enforces exiting. IUO's tend to cause bugs though, so it's generally best to avoid them even if in this case it will clearly always work.



The only alternative I know is:

```

func handleError(error: Error) {}

var error: Error?

if let error = error {

handleError(error: error)

return // not enforced

}

```

which nicely unwraps the optional, but does not force the exit.



Is there a way to both enforce the exit behavior and avoid the implicitly unwrapped optional?


Basically a `! let`, perhaps with different syntax, that could be used like:

```

guard ! let error = error else {

handleError(error: error)

return

}

```

Replies

FWIW, your first code fragment is not using an implicitly unwrapped optional. The "error" variable is just an optional, and the "!" operator is explicitly unwrapping it. This version of the same code would be using an IUO:


var error: Error! // <- this is the IUO
guard error == nil else {
     handleError(error: error)
     return
}


As to what you might use instead of the code pattern you find awkward, you might consider moving the test into the (or, an) error handling function:


func isNoError (_ error: Error?) -> Bool {
     guard let error = error else { return true }
     …
     return false
}
var error: Error?
guard isNoError (error) else { return }


However, I suspect that you might actually be trying to use a guard-return pattern where a try-catch pattern might be more appropriate. That would likely be a straightforward change if the error was thrown, or you might need to change other code to throw the error rather than returning it.

I suspect that you might actually be trying to use a guard-return pattern where a try-catch pattern might be more appropriate.

I have sympathy with browng’s point here because it crops up all the time in code that uses completion handlers. For example, in URLSession code like this:

let task = session.dataTask(with: request) { (data, response, error) in
    if let error = error {
        … handle transport error …
    } else {
        … deal with response from server …
    }
}

it would actually make sense to use a

guard
statement.

I don’t think there’s going to be a good fix for this until Swift grasps the concurrency nettle, which is unlikely to happen any time soon. In the meantime, if I were in browng’s shoes I’d probably look at adopting a standard approach for handling errors within my app, and then write code that converts any non-standard errors to my standard. I’ve talked about this before in a different context, but it is also the subject of much online discussion (including this recent article).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"