Reputation: 8861
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
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
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
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