Andy S
Andy S

Reputation: 8861

In Swift 2, how can I return JSON parsing errors to the completion block?

I would like to create a function in Swift 2 that gets data from a URL and returns it as a JSON object using NSURLSession. At first, this seemed pretty straight forward. I wrote the following:

func getJson(url:NSURL, completeWith: (AnyObject?,NSURLResponse?,NSError?)->Void) -> NSURLSessionTask? {

    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url) {
        (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in

        if error != nil {
            completeWith(nil, response, error)
        }

        if let data = data {

            do {
                let object:AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
            } catch let caught as NSError {
                completeWith(nil, response, caught)
            }

            completeWith(object, response, nil)

        } else {
            completeWith(nil, response, error)
        }
    }

    return task
}

However, that doesn't compile because the completion block doesn't declare "throws". The exact error is Cannot invoke 'dataTaskWithURL' with an argument list of type '(NSURL, (NSData?, NSURLResponse?, NSError?) throws -> Void)'. Even though I'm catching all errors in my do/catch statement, Swift still wants to propagate the NSError up the chain. The only way I can see around it is to use try!, like this:

if let data = data {

    let object:AnyObject? = try! NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
    completeWith(object, response, nil)

} else {
    completeWith(nil, response, error)
}

Now everything compiles just fine, but I've lost the NSError that's thrown by NSJSONSerialization.JSONObjectWithData.

Is there was I can capture the NSError potentially thrown by NSJSONSerialization.JSONObjectWithData and propagate it to the completion block without modifying the completion block's signature?

Upvotes: 11

Views: 13471

Answers (3)

Gerd Castan
Gerd Castan

Reputation: 6849

NSJSONSerialization throws ErrorType and not NSError.

so the correct code would be

do {
    let object:AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
} catch let caught as ErrorType {
    completeWith(nil, response, caught)
}

You also change your method signature to ErrorType.

For this reason, the accepted answer will always go into the "something else happened" block and will never report the error thrown by NSJSONSerialization.JSONObjectWithData.

Upvotes: 1

beachCode
beachCode

Reputation: 3510

to address the question from Jguffey. I saw the same error when I tried calling the function like this:

let taskResult = getJson(url!) { 
     (any: AnyObject,resp: NSURLResponse,error: NSError) in

it should be like this:

let taskResult = getJson(url!) { 
         (any: AnyObject?,resp: NSURLResponse?,error: NSError?) in

Upvotes: 2

Sergey A. Novitsky
Sergey A. Novitsky

Reputation: 485

I think, your catch is not exhaustive, so you need something like this:

do
{
  let object:AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
  completeWith(object, response, nil)
} catch let caught as NSError {
  completeWith(nil, response, caught)
} catch {
  // Something else happened.
  // Insert your domain, code, etc. when constructing the error.
  let error: NSError = NSError(domain: "<Your domain>", code: 1, userInfo: nil)
  completeWith(nil, nil, error)
}

Upvotes: 21

Related Questions