Swift, NSJSONSerialization and NSError

Swift, NSJSONSerialization and NSError

Updated for Swift 3

let jsonData = Data()
do {
    let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options:JSONSerialization.ReadingOptions(rawValue: 0))
    guard let dictionary = jsonObject as? Dictionary<String, Any> else {
        print(Not a Dictionary)
        // put in function
        return
    }
    print(JSON Dictionary! (dictionary))
}
catch let error as NSError {
    print(Found an error - (error))
}

Swift 2

let JSONData = NSData()
do {
    let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:NSJSONReadingOptions(rawValue: 0))
    guard let JSONDictionary: NSDictionary = JSON as? NSDictionary else {
        print(Not a Dictionary)
        // put in function
        return
    }
    print(JSONDictionary! (JSONDictionary))
}
catch let JSONError as NSError {
    print((JSONError))
}

The problem is that you cast the result of the JSON deserialization before
checking for an error. If the JSON data is invalid (e.g. incomplete) then

NSJSONSerialization.JSONObjectWithData(...)

returns nil and

NSJSONSerialization.JSONObjectWithData(...) as NSDictionary

will crash.

Here is a version that checks for the error conditions correctly:

var error:NSError? = nil
if let jsonObject: AnyObject = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:&error) {
    if let dict = jsonObject as? NSDictionary {
        println(dict)
    } else {
        println(not a dictionary)
    }
} else {
    println(Could not parse JSON: (error!))
}

Remarks:

  • The correct way to check for an error is to test the return value, not the
    error variable.
  • The JSON reading option .AllowFragments does not help here. Setting this option
    only allows that top-level objects that are not an instance of NSArray or NSDictionary, for example

    { someString }
    

You can also do it in one line, with an optional cast as?:

if let dict = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:nil) as? NSDictionary {
    println(dict)
} else {
    println(Could not read JSON dictionary)
}

The disadvantage is that in the else case you cannot distinguish whether reading
the JSON data failed or if the JSON did not represent a dictionary.

For an update to Swift 3, see LightningStryks answer.

Swift, NSJSONSerialization and NSError

Swift 3:

let jsonData = Data()
do {
    guard let parsedResult = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? NSDictionary else {
        return
    }
    print(Parsed Result: (parsedResult))
} catch {
    print(Error: (error.localizedDescription))
}

Leave a Reply

Your email address will not be published.