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 ofNSArray
orNSDictionary
, 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
Add Links to Swift Classes in the Quick Help Documentation Comments
Swift Uifont Ibinspectable - Is It Possible
In Swiftui How to Set the Environment Variable of Editmode in an Xcodepreview
How to Disable the Show Tab Bar Menu Option in Swiftui
How to Get the Edited Image from Uiimagepickercontroller in Swift
How to Loop Through All MAC Desktop Spaces
Add Shadow Above Swiftui's Tabview
Can/How to Replace My Kvo Stuff with Rc3
Check If Variable Is a Block/Function/Callable in Swift
Swift + Nsviewcontroller Background Color (MAC App)
Swiftui - How to Set the Title of a Navigationview to Large Title (Or Small)
Making Cocoa Application Scriptable Swift
Swift: How to Open File with Associated Application
Why Is My Swift Loop Failing with Error "Can't Form Range with End < Start"
Presenting a View Controller Programmatically in Swift