Issue with Swift 2 Error Handling - ios

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.
}

Related

NSJSONSerialization Error : Why am I not able to catch the error in try catch block? swift 3.0

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)
}

how to avail the catch concept in swift 2.0

I just converted my code to Swift 2.0 and it converted the code beautifully.
Just one thing I am not able to understand that how to use the try-catch in my codes.
I am posting the data and getting the JSON formatted
if anything goes wrong in network then it throws error and app crashes when JSON serialisation is done. How to prevent this, my code snap is below.
do {
json = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
}
catch {
// report error
}
It crashed in the line
json = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
How to use catch so that it does not crash here and shows error!!!
I'm using it in my project like
do{
if let result = try NSJSONSerialization.JSONObjectWithData(resultData, options: []) as? NSMutableDictionary{
}
}
catch let error as NSError{
print("Error : \(error)")
}
use this syntax, and remove the "!" in the try
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
}
catch _ {
// report error
print("Exception")
}
like diego said but the catch do like this to see what is the error you get in case you get one for debugging
catch let error as NSError{
print("Error : \(error)")
//or NSLog()
NSLog("Error = %#",error)
}

Crash while casting the result of JSONObjectWithData to Dictionary<String,Any>

The Application crashes while parsing JSON result to <String,Any> it does not enters the catch block:
do
{
result = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String,Any>
}
catch let error1 as NSError
{
result = nil
print(error1)
}
While if I casted the JSON result to <String,AnyObject> it works fine:
do
{
result = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String,AnyObject>
}
catch let error1 as NSError
{
result = nil
print(error1)
}
Is not the <Any> protocol accepts both the primitive types and the object types , then why it's failing ?
The reason you are crashing is the forced cast you are making. The do / catch construct in Swift doesn't work like exceptions in other languages, where any error will be caught in the catch block. In your case
NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
can throw an error, and the catch block will be called if an error is thrown. In contrast the forced cast to Dictionary does not throw an error if it fails. It would be far better to wrap the cast in an if let like so:
do
{
let temp = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
if let result = temp as? Dictionary<String,AnyObject> {
// Do something with the result
}
else {
// cast failed handle it gracefully
}
}
catch let error1 as NSError
{
result = nil
print(error1)
}
Now as to why the cast to "String, AnyObject" works while "String, Any" fails, I suspect it has to do with the fact that originally, it is an NSDictionary being returned from your JSON call. Those are bridged to Swift Dictionaries as "String, AnyObject". Now, what you are trying to do should technically work, and I consider it a bug that it doesn't. You might consider filing a radar for this behavior. In the meantime, it would also be advisable to avoid force casting as much as possible.
Don't use Any.
AnyObject covers all types JSON supports.

Swift: Extra argument 'error' in call

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)") }

JSON String to NSDictionary with Swift

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()

Resources