Handling exceptions in guard blocks - swift

I'm looking for an elegant way of combining guard blocks with do-try-catch semantics in Swift, and haven't been satisfied with my own efforts so far.
For background, I'm getting hold of some necessary data from a function that throws an exception and can return an Optional value. As a concrete example, let's say I'm looking up the most recent item from a TODO list stored on the filesystem. This can legitimately be nil (nothing on the list), or it can thrown an exception (some sort of I/O error accessing the filesystem), so both cases are distinct.
For the rest of the method though, we only care if there is a item to operate on - so this sounds like an ideal case for guard:
func myCoolMethod() {
guard let todoItem = try? fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}
This is indeed fine - until the guard block starts failing when you expect it to succeed, and you'd really like to (if nothing else) log the error message to try and investigate. As such, the try? needs to be replaced by a try to allow the error to be captured, and now we'll need to wrap the guard in a do block:
func myCoolMethod() {
do {
guard let todoItem = try fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}
catch {
// Now we know there was an error of some sort (can specialize the
// catch block if we need to be more precise)
// log error or similar, then...
return
}
}
There are two problems with this that I see:
The do block needs to wrap the whole method (due to the scope of todoItem). This means that the happy-path logic is indented (which is something that guard specifically helps to avoid); but additionally, it means that any errors from that logic will be caught by the catch block, perhaps accidentally. I'd much prefer the do block to only be scoped around the guard.
There's duplication of the "what to do if we have no item" block. In this trivial example it's just a return, but it still pains me a little to have two separate blocks that need to do broadly the same thing (where one just adds a little extra context with the error being available).
I'd like to get as close to this as possible:
func myCoolMethod() {
// `do guard` is made up but would enter the else block either if an exception
// is thrown, or the pattern match/conditional fails
do guard let todoItem = try fetchItemFromList() else {
if let error = error {
// Optional `error` is only bound if the guard failed due to an exception
// here we can log it etc.
}
// This part is common for both the case where an exception was thrown,
// and the case where the method successfully returned `nil`, so we can
// implement the common logic for handling "nothing to act on"
return
}
// rest of function acts on todoItem
...
}
What are my options here?

First, look for errors and deal with them. Then look for existence, and deal with that:
func myCoolMethod() {
// Making this `let` to be stricter. `var` would remove need for `= nil` later
let todoItem: Item?
// First check for errors
do {
todoItem = try fetchItemFromList()
} catch {
todoItem = nil // Need to set this in either case
// Error handling
}
// And then check for values
guard let todoItem = todoItem else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}
Or separate the problems into functions. When things get complicated, make more focused functions:
func myCoolMethod() {
// And then check for values
guard let todoItem = fetchItemAndHandleErrors() else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}
func fetchItemAndHandleErrors() -> Item? {
// First check for errors
do {
return try fetchItemFromList()
} catch {
// Error handling
return nil
}
}

Related

How to cancel an `async` function with cancellable type returned from `async` operation initiation

I need to support cancellation of a function that returns an object that can be cancelled after initiation. In my case, the requester class is in a 3rd party library that I can't modify.
actor MyActor {
...
func doSomething() async throws -> ResultData {
var requestHandle: Handle?
return try await withTaskCancellationHandler {
requestHandle?.cancel() // COMPILE ERROR: "Reference to captured var 'requestHandle' in concurrently-executing code"
} operation: {
return try await withCheckedThrowingContinuation{ continuation in
requestHandle = requester.start() { result, error in
if let error = error
continuation.resume(throwing: error)
} else {
let myResultData = ResultData(result)
continuation.resume(returning: myResultData)
}
}
}
}
}
...
}
I have reviewed other SO questions and this thread: https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/4
There are cases that are very similar, but not quite the same. This code won't compile because of this error:
"Reference to captured var 'requestHandle' in concurrently-executing code"
I assume the compiler is trying to protect me from using the requestHandle before it's initialized. But I'm not sure how else to work around this problem. The other examples shown in the Swift Forum discussion thread all seem to have a pattern where the requester object can be initialized before calling its start function.
I also tried to save the requestHandle as a class variable, but I got a different compile error at the same location:
Actor-isolated property 'profileHandle' can not be referenced from a
Sendable closure
You said:
I assume the compiler is trying to protect me from using the requestHandle before it’s initialized.
Or, more accurately, it is simply protecting you against a race. You need to synchronize your interaction with your “requester” and that Handle.
But I’m not sure how else to work around this problem. The other examples shown in the Swift Forum discussion thread all seem to have a pattern where the requester object can be initialized before calling its start function.
Yes, that is precisely what you should do. Unfortunately, you haven’t shared where your requester is being initialized or how it was implemented, so it is hard for us to comment on your particular situation.
But the fundamental issue is that you need to synchronize your start and cancel. So if your requester doesn’t already do that, you should wrap it in an object that provides that thread-safe interaction. The standard way to do that in Swift concurrency is with an actor.
For example, let us imagine that you are wrapping a network request. To synchronize your access with this, you can create an actor:
actor ResponseDataRequest {
private var handle: Handle?
func start(completion: #Sendable #escaping (Data?, Error?) -> Void) {
// start it and save handle for cancelation, e.g.,
handle = requestor.start(...)
}
func cancel() {
handle?.cancel()
}
}
That wraps the starting and canceling of a network request in an actor. Then you can do things like:
func doSomething() async throws -> ResultData {
let responseDataRequest = ResponseDataRequest()
return try await withTaskCancellationHandler {
Task { await responseDataRequest.cancel() }
} operation: {
return try await withCheckedThrowingContinuation { continuation in
Task {
await responseDataRequest.start { result, error in
if let error = error {
continuation.resume(throwing: error)
} else {
let resultData = ResultData(result)
continuation.resume(returning: resultData)
}
}
}
}
}
}
You obviously can shift to unsafe continuations when you have verified that everything is working with your checked continuations.
After reviewing the Swift discussion thread again, I see you can do this:
...
var requestHandle: Handle?
let onCancel = { profileHandle?.cancel() }
return try await withTaskCancellationHandler {
onCancel()
}
...

Completion handler never called

I have a function like this.
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any) -> Void)){
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
}
I am not putting code to handle the result if it fails. Is that a bad thing? Do I need to have a completion handler in the case that the call fails so that this function does not stay in the memory waiting for that completion handler to be called? What is the best practice?
In general, it is a good practice to call the completion on every case. The reason for this is that you usually want to let the upper lever(business logic layer) decide if it should mark some balances(for example) as saved, or maybe show a dialog when an error has occurred. That is a good practice with the thinking that everything should be a module. That being said, if another module will want to call the same function at some point, it may be a good thinking to let that module what happened with the result. That can be implemented in several ways, I won't enter here, it's your decision.
However, it's not a must to do it. If a block won't be called it should be deallocated, and then everything is good memory-wise. So in your example, if you don't retain the block somewhere else(for example holding it in a variable inside the class that makes getAccountBalances call), you should be just fine.
Another important part is when you call the function be careful to not create a memory leak where you retain the self inside the block:
getAccountBalances() { _ in
self.updateUI()
}
This block will create a retain to self and if everything goes okay with the call, but user left the screen, you may end up using variables that were deallocated and crash the app. A good practice here is to not retain the self in the callback, but make it weak before that:
getAccountBalances() { [weak self] _ in
// This is not necessarily needed, and you could use self?.updateUI() instead.
// However, this is usually another thing i like to do and consider it a good practice
guard let `self` = self else { return }
self.updateUI()
}
Your title: Completion handler never called
Your code: does not return any value to the completion handler if there is an error.
How would you expect to see a result of the competion handler if there is a failure? Would you like to crash the app? This way is better because it handles both cases:
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any?) -> Void)){ //make Any an optional
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
completionHandler(nil)
}
Your new call to this function:
getAccountBalances() { value in
guard let _value = value else { // anticipate on the error return }
// use _value, the balances are inside.
}
An other approach would be not making it nil, but downcasting the value inside of it. That would look like this:
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any) -> Void)){
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
completionHandler(error) //notice this change
}
Then your function would look like this:
getAccountBalances() { value in
if let error = value as? Error { //or whatever type your error is in the function
//there is an error
}
}

Returning value for function that throws

I've got a pretty simple question but I could only find a similar answer in C++ thread. Compiler complains on getUserFromServer function stating that it requires return value even though it can catch an exception. It is obvious WHY it asks for return value but I'm not sure how to handle the situation properly.
One way is to create some sort of dummy user and return it but it doesn't sound any good. Another way is by making return type Optional but it's just moving responsibility for nil-checks to another level, which is probably not a good idea. Any help would help.
Here's a setup to simulate behaviour I expect in real code.
enum TestError: Error {
case JustError
}
class User {}
class ServerClient {
func getUser(really: Bool) throws -> User {
var uza: User?
if really {
uza = User()
}
guard let user = uza else {
throw TestError.JustError
}
return user
}
}
func getUserFromServer() -> User {
let client = ServerClient()
let user: User
do {
user = try client.getUser(really: true)
return user
} catch TestError.JustError {
print("just an error occured")
} catch {
print("something terrible happened")
}
} //Error: missing return in a function
So imagine I'm a the caller, and I write this line of code:
let user = getUserFromServer()
print(user)
It fails somehow. Great. What do you want to happen? What should user be? What should print? (There's no right answer here; the question really is what do you want it to be?)
If you want user to be nil, then getUserFromServer should return User?. If you want user to be some default user, then you'll need a way to create that. If you want the caller to not print at all (and instead handle some error), then getUserFromServer() should throw. If you want it to crash, then you need to add a fatalError inside getUserFromServer. These are all valid, but it has to do something, and what that something is, is up to you.
That said, if the question is just advice about what it should be, looking at this code you should probably default to making it an optional unless you need some other behavior. That would be the "normal" thing here since you already "handled" the error.

Is there a way to throw errors from asynchronous closures in Swift 3?

I’m executing some functions in a test asynchronously using a DispatchQueue like this:
let queue: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated)
let group: DispatchGroup = DispatchGroup()
func execute(argument: someArg) throws {
group.enter()
queue.async {
do {
// Do stuff here
group.leave()
} catch {
Log.info(“Something went wrong")
}
}
group.wait()
}
Sometimes the code inside the do block can throw errors, that I have to catch later on. Since I’m developing a test, I want it to fail, if the code inside the do block throws an error.
Is there a way to throw an error, without catching it inside the queue.async call?
You cannot throw an error, but you can return an error:
First, you need to make your calling function asynchronous as well:
func execute(argument: someArg, completion: #escaping (Value?, Error?)->()) {
queue.async {
do {
// compute value here:
...
completion(value, nil)
} catch {
completion(nil, error)
}
}
}
The completion handler takes a parameter which we could say is a "Result" containing either the value or an error. Here, what we have is a tuple (Value?, Error?), where Value is the type which is calculated by the task. But instead, you could leverage a more handy Swift Enum for this, e.g. Result<T> or Try<T> (you might want to the search the web).
Then, you use it as follows:
execute(argument: "Some string") { value, error in
guard error == nil else {
// handle error case
}
guard let value = value else {
fatalError("value is nil") // should never happen!
}
// do something with the value
...
}
Some rules that might help:
If a function calls an asynchronous function internally, it inevitable becomes an asynchronous function as well. *)
An asynchronous function should have a completion handler (otherwise, it's some sort of "fire and forget").
The completion handler must be called, no matter what.
The completion handler must be called asynchronously (with respect the the caller)
The completion handler should be called on a private execution context (aka dispatch queue) unless the function has a parameter specifying where to execute the completion handler. Never use the main thread or main dispatch queue - unless you explicitly state that fact in the docs or you intentionally want to risk dead-locks.
*) You can force it to make it synchronous using semaphores which block the calling thread. But this is inefficient and really rarely needed.
Well, you might conclude, that this looks somewhat cumbersome. Fortunately, there's help - you might look for Future or Promise which can nicely wrap this and make the code more concise and more comprehensible.
Note: In Unit Test, you would use expectations to handle asynchronous calls (see XCTest framework).
Refactor your code to use queue.sync and then throw your error from there. (Since your execute function is actually synchronous, given the group.wait() call at the last line, it shouldn't really matter.)
For instance, use this method from DispatchQueue:
func sync<T>(execute work: () throws -> T) rethrows -> T
By the way, a good idiom for leaving a DispatchGroup is:
defer { group.leave() }
as the first line of your sync/async block, which guarantees you won't accidentally deadlock when an error happens.

Swift do-try-catch syntax

I give it a try to understand new error handling thing in swift 2. Here is what I did: I first declared an error enum:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
And then I declared a method that throws an error (not an exception folks. It is an error.). Here is that method:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
The problem is from the calling side. Here is the code that calls this method:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}
After the do line compiler says Errors thrown from here are not handled because the enclosing catch is not exhaustive. But in my opinion it is exhaustive because there is only two case in SandwichError enum.
For regular switch statements swift can understands it is exhaustive when every case handled.
There are two important points to the Swift 2 error handling model: exhaustiveness and resiliency. Together, they boil down to your do/catch statement needing to catch every possible error, not just the ones you know you can throw.
Notice that you don't declare what types of errors a function can throw, only whether it throws at all. It's a zero-one-infinity sort of problem: as someone defining a function for others (including your future self) to use, you don't want to have to make every client of your function adapt to every change in the implementation of your function, including what errors it can throw. You want code that calls your function to be resilient to such change.
Because your function can't say what kind of errors it throws (or might throw in the future), the catch blocks that catch it errors don't know what types of errors it might throw. So, in addition to handling the error types you know about, you need to handle the ones you don't with a universal catch statement -- that way if your function changes the set of errors it throws in the future, callers will still catch its errors.
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}
But let's not stop there. Think about this resilience idea some more. The way you've designed your sandwich, you have to describe errors in every place where you use them. That means that whenever you change the set of error cases, you have to change every place that uses them... not very fun.
The idea behind defining your own error types is to let you centralize things like that. You could define a description method for your errors:
extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}
And then your error handling code can ask your error type to describe itself -- now every place where you handle errors can use the same code, and handle possible future error cases, too.
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}
This also paves the way for error types (or extensions on them) to support other ways of reporting errors -- for example, you could have an extension on your error type that knows how to present a UIAlertController for reporting the error to an iOS user.
I suspect this just hasn’t been implemented properly yet. The Swift Programming Guide definitely seems to imply that the compiler can infer exhaustive matches 'like a switch statement'. It doesn’t make any mention of needing a general catch in order to be exhaustive.
You'll also notice that the error is on the try line, not the end of the block, i.e. at some point the compiler will be able to pinpoint which try statement in the block has unhandled exception types.
The documentation is a bit ambiguous though. I’ve skimmed through the ‘What’s new in Swift’ video and couldn’t find any clues; I’ll keep trying.
Update:
We’re now up to Beta 3 with no hint of ErrorType inference. I now believe if this was ever planned (and I still think it was at some point), the dynamic dispatch on protocol extensions probably killed it off.
Beta 4 Update:
Xcode 7b4 added doc comment support for Throws:, which “should be used to document what errors can be thrown and why”. I guess this at least provides some mechanism to communicate errors to API consumers. Who needs a type system when you have documentation!
Another update:
After spending some time hoping for automatic ErrorType inference, and working out what the limitations would be of that model, I’ve changed my mind - this is what I hope Apple implements instead. Essentially:
// allow us to do this:
func myFunction() throws -> Int
// or this:
func myFunction() throws CustomError -> Int
// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int
Yet Another Update
Apple’s error handling rationale is now available here. There have also been some interesting discussions on the swift-evolution mailing list. Essentially, John McCall is opposed to typed errors because he believes most libraries will end up including a generic error case anyway, and that typed errors are unlikely to add much to the code apart from boilerplate (he used the term 'aspirational bluff'). Chris Lattner said he’s open to typed errors in Swift 3 if it can work with the resilience model.
Swift is worry that your case statement is not covering all cases, to fix it you need to create a default case:
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch Default {
print("Another Error")
}
I was also disappointed by the lack of type a function can throw, but I get it now thanks to #rickster and I'll summarize it like this: let's say we could specify the type a function throws, we would have something like this:
enum MyError: ErrorType { case ErrorA, ErrorB }
func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }
do {
try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }
The problem is that even if we don't change anything in myFunctionThatThrows, if we just add an error case to MyError:
enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }
we are screwed because our do/try/catch is no longer exhaustive, as well as any other place where we called functions that throw MyError
enum NumberError: Error {
case NegativeNumber(number: Int)
case ZeroNumber
case OddNumber(number: Int)
}
extension NumberError: CustomStringConvertible {
var description: String {
switch self {
case .NegativeNumber(let number):
return "Negative number \(number) is Passed."
case .OddNumber(let number):
return "Odd number \(number) is Passed."
case .ZeroNumber:
return "Zero is Passed."
}
}
}
func validateEvenNumber(_ number: Int) throws ->Int {
if number == 0 {
throw NumberError.ZeroNumber
} else if number < 0 {
throw NumberError.NegativeNumber(number: number)
} else if number % 2 == 1 {
throw NumberError.OddNumber(number: number)
}
return number
}
Now Validate Number :
do {
let number = try validateEvenNumber(0)
print("Valid Even Number: \(number)")
} catch let error as NumberError {
print(error.description)
}
Error can be handle using switch case in catch
func checkAge(age:Int) throws {
guard !(age>0 && age < 18) else{
throw Adult.child
}
guard !(age >= 60) else{
throw Adult.old
}
guard (age>0) else{
throw Adult.notExist
}
}
do{
try checkAge(age:0)
}
catch let error {
switch error{
case Adult.child : print("child")
case Adult.old : print("old")
case Adult.notExist : print("not Exist")
default:
print("default")
}
}
enum Adult:Error {
case child
case old
case notExist
}
Create enum like this:
//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}
Create method like:
func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
throw spendingError.limit
}
return morningSpending + eveningSpending
}
Now check error is there or not and handle it:
do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}