Dawy
Dawy

Reputation: 938

Throwing errors from closure

I have this piece of code in my app:

func saveContact2(contact: String) throws {
    let contactStore = CNContactStore()
    contactStore.requestAccess(for: .contacts, completionHandler: {(granted, error) in
        if granted && error == nil {
            //...
        } else {
            if !granted {
                throw contactErrors.contactAccessNotGranted(["Error","Access to Contacts is not granted."])
            }
        }
    })
}

I'd like to throw all errors raising in closure to calling function.

Compiler shows error:

Invalid conversion from throwing function of type '(_, _) throws -> ()' to non-throwing function type '(Bool, Error?) -> Void'

Could anyone help me please with the right syntax?

Upvotes: 1

Views: 248

Answers (1)

Rob
Rob

Reputation: 438487

You cannot throw errors from an @escaping closure that is called asynchronously. And this makes sense because your app has carried on with its execution and there’s no where to catch the error.

So, instead, adopt completion handler pattern yourself:

func saveContact2(_ contact: String, completion: @escaping: (Result<Bool, Error>) -> Void) {
    let contactStore = CNContactStore()
    contactStore.requestAccess(for: .contacts) { (granted, error) in
        guard granted else {
            completion(.failure(error!)
            return
        }

        //...
        completion(.success(true))
    }
}

And then you’d call it like:

saveContact2(contactName) { result in 
    switch result {
    case .failure:
        // handler error here

    case .success:
        // handle confirmation of success here
    }
}

If you’re using an old compiler that doesn’t have the Result type, it’s basically:

enum Result<Success, Failure> where Failure: Error {
    case success(Success)
    case failure(Failure)
}

Upvotes: 1

Related Questions