Swift, Nsjsonserialization and Nserror

Swift, NSJSONSerialization and NSError

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 LightningStryk's answer.

Trying to use NSJSONSerialization.JSONObjectWithData on Swift 2

Looks like the issue is in the catch statement. The following code won't produce the error you've described.

do {
jsonResults = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
// success ...
} catch {
// failure
print("Fetch failed: \((error as NSError).localizedDescription)")
}

I do realize that the code you've provided is supposed to be correct, so you should consider filing a bug with Apple about this.

Swift NSJSONSerialization.JSONObjectWithData doesn't handle empty Data

Don't say as! NSMutableArray. The term as! means "crash me", so you can hardly be surprised when you do crash. Say as? NSMutableArray. Test the result for nil. If it is nil, stop.

if let jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as? NSMutableArray {
self.jsonResult = jsonResult
}

You may still have problems because this is never going to be a mutable array; it's just an array. So you might have to change it to:

if let jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as? NSArray {
self.jsonResult = NSMutableArray(array: jsonResult)
}

(But, as vadian has said in a comment, it would be even better if you could abandon use of NSArray and NSMutableArray and use Swift types instead.)

Swift 2 - NSJSONSerialization.JSONObjectWithData Handling Error

swift3

NSJSONSerialization and its methods are modified, according to the Swift Documents.

do {

let JsonDict = try JSONSerialization.jsonObject(with: data, options: [])
// you can now use t with the right type
if let dictFromJSON = JsonDict as? [String:String]
{
// use dictFromJSON
}
} catch let error as NSError {
print(error)
}

Swift2

   init(data: NSData){  // ~this chunk~
do {
self.json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String:AnyObject]

} catch {
print("error: \(error)")
self.json = nil
}
}

for more information tutorial1, tutorial2

Correct handling of NSJSONSerialization (try catch) in Swift (2.0)?

The jsonObject can throw errors, so put it within do block, use try, and catch any errors thrown. In Swift 3:

do {
let anyObj = try JSONSerialization.jsonObject(with: data) as! [String: Any]

let label = anyObj["label"] as! String
let value = anyObj["value"] as! Int
let uprate = anyObj["uprate"] as! Int
let sufix = anyObj["sufix"] as! String

let props = Fieldpropertie(label: label, value: value, uprate: uprate, sufix: sufix)

// etc.
} catch {
print("json error: \(error.localizedDescription)")
}

Or, in Swift 4, you can simplify your code by making your struct conform to Codable:

struct Fieldpropertie: Codable {
let label: String
let value: Int
let uprate: Int
let suffix: String
}

Then

do {
let props = try JSONDecoder().decode(Fieldpropertie.self, from: data)
// use props here; no manual parsing the properties is needed
} catch {
print("json error: \(error.localizedDescription)")
}

For Swift 2, see previous revision of this answer.

Need to adjust NSJSONSerialization to iOS10

Even in iOS 9, there was no guarantee NSJSONSerialization.JSONObjectWithData(_:options:) would return mutable object or not. You should have specified NSJSONReadingOptions.MutableContainers.

And in your code, you are not modifying jsonResult, which means you have no need to declare it as NSMutableArray. Just replace NSMutableArray to NSArray, and then you have no need to specify NSJSONReadingOptions.MutableContainers.

But as vadian is suggesting, you better use Swift types rather than NSArray or NSDictionary. This code should work both in iOS 9 and 10.

func parseJSON() {

var jsonResult: [[String: AnyObject]] = [] //<- use Swift type

do{
try jsonResult = NSJSONSerialization.JSONObjectWithData(self.data, options: []) as! [[String: AnyObject]] //<- convert to Swift type, no need to specify options

} catch let error as NSError {
print(error)
}

var downloadedLists: [ContactsList] = []

for jsonElement in jsonResult { //<- your for-in usage can be simplified

let tempContactsList = ContactsList()

//the following insures none of the JsonElement values are nil through optional binding
let id = jsonElement["id"] as? String
let name = jsonElement["name"] as? String
let pin = jsonElement["pin"] as? String
let lastUpdated = jsonElement["created"] as? String
let listAdminDeviceID = jsonElement["admin"] as? String

tempContactsList.id = id
tempContactsList.name = name
tempContactsList.pin = pin
tempContactsList.lastUpdated = lastUpdated
tempContactsList.listAdmin = listAdminDeviceID

downloadedLists.append(tempContactsList)

}

dispatch_async(dispatch_get_main_queue(), { () -> Void in

self.delegate.listsDownloadingComplete(downloadedLists)

})
}

Try this and check it on iOS 10 devices.

(The as! conversion would cause some weird crashes when your server is malfunctioning, but that would be another issue, so I keep it there.)

NSJSONSerialization Causing Crash in Swift when Verifying App Receipt

I am guessing the line which the debugger cursor is pointing at is:

let anyObj = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [String:AnyObject]

...and your data variable is nil (i.e., optional contains no value). Check that before force-unwrapping (! operator).

(The (error as NSError).localizedDescription can't be, because error is supposed to be non-nil inside the catch block and failing to case to NSError would give a different error message, I think).



Edit:

This is how you unwrap safely:

if let safeData = data { // <-- YOU NEED TO DO THIS
// data is 'NSData?', safeData is 'NSData'.

// If data contained any value, it is now assigned to safeData.
// Use safeData as the source of your JSON (**not** data).
}
else{
print("error: data is nil!")
}

Swift, NSJSONSerialization.dataWithJSONObject BAD_EXEC_ACCESS while Converting to JSON

Resolved it

    var newCompanyArray:Array<AnyObject> = []
var dict = Dictionary<String,String>()
var jsonError: NSError?

print("loadCompaniesFromSynch >> Companies Count \(self.companies.count)")

for cmp in self.companies {
dict["ORG_CODE"] = cmp.orgCode
dict["ORG_DESCRIPTION"] = cmp.orgDescription
dict["OBX_B1L_CODE"] = cmp.orgBtlCode
dict["1TL_DESCRIPTION"] = cmp.orgBtlDescription
dict["1RG_STATUS"] = cmp.orgStatus

newCompanyArray.append(dict)
}

print(NSJSONSerialization.isValidJSONObject(newCompanyArray))
var dataFinal:NSData = NSJSONSerialization.dataWithJSONObject(newCompanyArray, options: nil, error: &jsonError)!


Related Topics



Leave a reply



Submit