I'm currently developing my first iOS app using Swift 2.0 and Xcode Beta 2. It reads an external JSON and generates a list in a table view with the data. However, I'm getting a strange little error that I can't seem to fix:
Extra argument 'error' in call
Here is a snippet of my code:
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
print("Task completed")
if(error != nil){
print(error!.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
if(err != nil){
print("JSON Error \(err!.localizedDescription)")
}
if let results: NSArray = jsonResult["results"] as? NSArray{
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.appsTableView!.reloadData()
})
}
}
})
The error is thrown at this line:
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
Can someone please tell me what I'm doing wrong here?
With Swift 2, the signature for NSJSONSerialization has changed, to conform to the new error handling system.
Here's an example of how to use it:
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
With Swift 3, the name of NSJSONSerialization and its methods have changed, according to the Swift API Design Guidelines.
Here's the same example:
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject] {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
Things have changed in Swift 2, methods that accepted an error parameter were transformed into methods that throw that error instead of returning it via an inout parameter. By looking at the Apple documentation:
HANDLING ERRORS IN SWIFT:
In Swift, this method returns a nonoptional result and is marked with the throws keyword to indicate that it throws an error in cases of failure.
You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language (Swift 2.1) and Error Handling in Using Swift with Cocoa and Objective-C (Swift 2.1).
The shortest solution would be to use try? which returns nil if an error occurs:
let message = try? NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
If you're also interested into the error, you can use a do/catch:
do {
let message = try NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
} catch let error as NSError {
print("An error occurred: \(error)")
}
This has been changed in Swift 3.0.
do{
if let responseObj = try JSONSerialization.jsonObject(with: results, options: .allowFragments) as? NSDictionary{
if JSONSerialization.isValidJSONObject(responseObj){
//Do your stuff here
}
else{
//Handle error
}
}
else{
//Do your stuff here
}
}
catch let error as NSError {
print("An error occurred: \(error)") }
Related
I am calling REST API to get a response from the server and also testing for bad data type to caught in try catch block nut app still crashes with below error:
Could not cast value of type '__NSArrayM' (0x10575ee00) to 'NSDictionary' (0x10575f2d8).
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error!)
return
}
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,String>
print(responseObject)
} catch let jsonError {
print(jsonError)
}
}
dataTask.resume()
For this, I also found the solution that I should check data with JSON format first then use JSONSerialization.
if JSONSerialization.isValidJSONObject(data){
responseObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,String>
}
else {
print("Error")
}
I want to know that if type cast value error not caught by the try-catch block then what is a use of try-catch block in network call. I can simply check for the JSON format and the pass the response object.
What is a good programming practice?
NOTE : Error is not an issue I just don't want my should crash an app when data type changes form dictionary to array or other than specified type.
This is not about valid JSON, you are getting this crash because your JSON at root is Array not Dictionary and you are trying to cast the result of jsonObject(with:) to Dictionary that is the reason you are getting this crash also there is no need to specify mutableContainers with Swift.
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as! [[String:Any]]
print(responseObject)
} catch let jsonError {
print(jsonError)
}
Edit: If you don't know your JSON time then you can use if let like this way.
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: [])
if let responseArray = responseObject as? [[String:Any]] {
//Access array
}
else if let responseDictionary = responseObject as? [String:Any] {
//Access dictionary
}
print(responseObject)
} catch let jsonError {
print(jsonError)
}
I'm working on a app in which the api is called through NSURLSession. When the Api works correctly there is no problem but when no data is received due to any error then after Serialization it throws error but the else block for it is never called
let task = session.dataTaskWithRequest(request) { (let data, let response, let error) in
do {
guard let data:NSData = data , let response: NSURLResponse = response where error == nil else {
throw error!
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else{
print("Serialization failed") //This block never executes even if the Serialization Fails
throw JSONError.ConversionFailed
}
guard json.valueForKey("success") != nil else {
return
}
self.apidata = json
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
print(json.valueForKey("success")!)
}
catch let error as JSONError{
self.showalertview(error.rawValue)
print(error.rawValue)
} catch let error as NSError{
print(error.debugDescription)
}
}
task.resume()
What I'm doing wrong here???
Consider:
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
}
} catch {
// B
}
If NSJSONSerialization throws an error (i.e. if it wasn't really a JSON response or if the response was malformed), it will proceed directly to B and the guard statement doesn't come into play. The guard statement will only execute A if and only if (a) the NSJSONSerialization call, itself, didn't throw any errors (i.e. the JSON was well-formed); but (b) the cast to the the dictionary failed (e.g. the top level JSON object was an array instead of a dictionary). That's an extremely unlikely scenario (your server would have to accidentally return a well formed JSON response that was not a dictionary, e.g. a JSON array).
To accomplish what you want, you would use try? to make sure that NSJSONSerialization wouldn't throw any errors, itself:
do {
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
throw JSONError.ConversionFailed
}
} catch {
// B
}
By doing this, only if A performs a throw will B be called
This question already has answers here:
Correct handling of NSJSONSerialization (try catch) in Swift (2.0)?
(3 answers)
Closed 6 years ago.
I have a JSONParser, but unfortunately I couldn't adapt NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) bit to Swift 2.0, so I am receiving error:
Extra argument 'error' in call
I found out that I can achieve this with do-try-catch, but I couldn't figure out how to adapt it in my case. Whatever I tried is just throwing another error.
class JSONParser {
let json: AnyObject?
var error: NSError?
init(data: NSData){ // ~this chunk~
self.json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error)
}
func array()->NSArray?{
if let jsonResponse: AnyObject = self.json{
return jsonResponse as? NSArray
}
return nil
}
func dictionary()->NSDictionary?{
if let jsonResponse: AnyObject = self.json{
return jsonResponse as? NSDictionary
}
return nil
}
}
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
var dataSource = [AnyObject]()
do { self.dataSource = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.MutableLeaves) as! [AnyObject]
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
if self.dataSource.count != 0 {
dispatch_async(dispatch_get_main_queue()) {
self.sampleTableView.reloadData()
}
}
I am using REST to get JSON data and then parse it. To do this I am using NSJSONObjectWithData, as far as I'm aware this method used to have an error handler within its parameters but it's no longer there. In my code here:
let err: NSError?
let options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers
var jsonResult = NSJSONSerialization.JSONObjectWithData(data!, options: options) as! NSDictionary;
I get an error which says:
"Call can throw, but it is not marked with 'try' and the error is not handled"
How would I go about fixing this error?
Here is the correct implementation,
do {
let jsonDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
//Use your dictionary here.
print("JSON : \(jsonDictionary)")
}
catch {
print(error)
//Handle any error.
}
I am trying to create a dictionary from data that is held in a server, I receive the data but I cannot convert the data to an NSDictionary, I believe it is held in an NSData Object
let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData!, options: nil, error: &error) as NSDictionary
This line of code is the one giving me the problem, it throws a BAD_EXEC_INSTRUCTION.
MY Question: How can I turn a JSON into an NSDictionary?
Your code does not do any error handling. But it can (and if this data comes from a web service, will) fail in multiple ways.
You have to make sure that your data object actually exists
You have to make sure that the data object can be converted to JSON
You have to make sure that the JSON actually contains a Dictionary
You should use Swifts conditional cast and it's optional binding capabilities.
The optional binding if let JSONData = JSONData checks that JSONData is not nil. The force unwrap (JSONData!) you use might crash if no data could be received.
The optional binding if let json = NSJSONSerialization.JSONObjectWithData checks if the data could be converted to a JSON object. The conditional cast as? NSDictionary checks if the JSON object is actually a dictionary. You currently don't use these checks, you cast the objects as NSDictionary. Which will crash, if the object is not valid json, or if its not a dictionary.
I would recommend something like this:
var error: NSError?
if let JSONData = JSONData { // Check 1
if let json: AnyObject = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) { // Check 2
if let jsonDictionary = json as? NSDictionary { // Check 3
println("Dictionary received")
}
else {
if let jsonString = NSString(data: JSONData, encoding: NSUTF8StringEncoding) {
println("JSON String: \n\n \(jsonString)")
}
fatalError("JSON does not contain a dictionary \(json)")
}
}
else {
fatalError("Can't parse JSON \(error)")
}
}
else {
fatalError("JSONData is nil")
}
You could merge check 2 and 3 into one line and check if NSJSONSerialization can create a NSDictionary directly:
var error: NSError?
if let JSONData = JSONData { // Check 1.
if let JSONDictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as? NSDictionary { // Check 2. and 3.
println("Dictionary received")
}
else {
if let jsonString = NSString(data: JSONData, encoding: NSUTF8StringEncoding) {
println("JSON: \n\n \(jsonString)")
}
fatalError("Can't parse JSON \(error)")
}
}
else {
fatalError("JSONData is nil")
}
Make sure to replace fatalError with appropriate error handling in your production code
Update for Swift 2.
Now you must use it inside a try catch block.
do {
let responseObject = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String:AnyObject]
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
Here jsonResult will give you the response in NSDictionary:
let url = NSURL(string: path)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
println("Task completed")
if(error != nil) {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
})
task.resume()