Nicolas Miari
Nicolas Miari

Reputation: 16256

Call function that throws and attempt to cast the returned value, abort silently if either fails

I'm guarding the call to a method that may throw (a CoreData fetch request), and at the same time I'm casting the returned value to a specific type of array:

guard let results = try? managedContext.executeFetchRequest(fetchRequest) as? [MyManagedObjectClass] else {

    // Bail out if fetch or cast fails:
    return
}

The way I understand it is that, because I'm using guard/try?/else (instead of do/try/catch), the else block (i.e. return statement) above will be executed if either of the following occurs:

  1. The fetch request fails (and throws)
  2. The fetch request succeeds but casting of the returned value to [MyManagedObjectClass] fails.

The code above works, but the after control successfully passes the guard check, the variable results ends up as being of type [MyManagedObjectClass]? ("optional array of MyManagedObjectClass").

So if I want to -say- loop through the array elements I have to first unwrap it:

if let results = results {
    // now, 'results' is of type:
    // "(non-optional) Array of MyManagedObjectClass" 

    for element in results {
        // (process each element...)
    }
}

I could use as! instead of as? when casting after the guard statement; that makes results a non-optional array.

But then, if the fetch succeeds but the cast fails, It will still trigger a runtime error (crash) instead of control flowing to the else block, right?.

Is there a smarter way? (catch both errors, end up with a non-optional array)


Note: I understand that lumping both failure causes (fetch and cast) together into one else block is not the best design, and perhaps I should check for both separately (perhaps I should use the more traditional do/try/catch), but I would like to better grasp the details of this complex construct.

Upvotes: 2

Views: 61

Answers (1)

Cristik
Cristik

Reputation: 32833

You need to wrap the try call into parentheses:

let results = (try? managedContext.executeFetchRequest(fetchRequest)) as? [MyManagedObjectClass]

otherwise try? will apply to the whole expression, generating an optional value that gets assigned to results.

Upvotes: 3

Related Questions