Hope Hope - 2 months ago 8
Swift Question

Swift, NSJSONSerialization and NSError

The problem is when there is incomplete data

NSJSONSerialization.JSONObjectWithData
is crashing the application giving
unexpectedly found nil while unwrapping an Optional value
error instead of informing us using NSError variable. So we are unable to prevent crash.

You can find code we are using below

var error:NSError? = nil

let dataToUse = NSJSONSerialization.JSONObjectWithData(receivedData, options: NSJSONReadingOptions.AllowFragments, error:&error) as NSDictionary

if error != nil { println( "There was an error in NSJSONSerialization") }


Till now we are unable to find a work around.

Answer

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.

Comments