10

In swift how do I throw an error within a completion handler like this:

    let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {
        (data, response, error) in
        do {
            //something
            completion(result)
        } catch let jsonError {
            throw CustomError.myerror //THIS DOESN'T WORK
        }
    })
    task.resume()

as the error is

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

Wayneio
  • 3,466
  • 7
  • 42
  • 73
  • 2
    Possible duplicate of [Can't get throws to work with function with completion handler](https://stackoverflow.com/questions/33402348/cant-get-throws-to-work-with-function-with-completion-handler) – Enea Dume Jul 09 '18 at 10:09

1 Answers1

23

Short story: You can't throw in a dataTask completion closure

You could return two values in the completion handler

...completion: @escaping (ResultType?, Error?)->Void

and return

completion(result, nil)
completion(nil, CustomError.myerror)

or more convenient use an enum with associated type

enum Result {
    case success(ResultType), failure(Error)
}

...completion: @escaping (Result)->Void

and return

completion(.success(result))
completion(.failure(CustomError.myerror))

You can process the result

foo() { result in
    switch result {
    case .success(let resultType): // do something with the result
    case .failure(let error): // Handle the error
    }
}

Update:

In Swift 5 using the new built-in Result type it's even more comfortable because Result can capture the result of the throwing expression

...completion: @escaping (Result<MyType,Error>)->Void

let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {
    (data, response, error) in

    completion(Result { try something()})
})
task.resume()

Update 2:

With async/await completion handlers are gone

do {
    let (data, response) = try await URLSession.shared.data(for: request)
} catch {
     throw CustomError.myerror
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Awesome, thanks. I used the latter option. How do I check the type of result, like if the result is of type .success or .failure – Wayneio Jul 09 '18 at 10:35
  • @vadian, I'm not sure I follow how the code you've suggested gets around the type signature for the dataTask - `(Data?, URLResponse?, Error?) -> Void` Would you be able to post how you take this code - https://pastecode.xyz/view/de5fe2cc, and get it to return the enum like you mentioned? – user2779581 Dec 03 '18 at 23:00