Marty Griffin
Marty Griffin

Reputation: 349

Crash/Error when trying to convert valid JSON data from NSURLConnection to NSDictonary in Swift

I have been trying to get a sample Rest request working on a sample swift iOS app I am working on. I have pulled from a few tutorials I have seen that said this works but my app seems to crash at

 var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary

The exception is thrown in the swift_dynamicCast library. I can convert my response to a String without issues and the string is a valid json array. Any Ideas?

Here is the full code for my test function.

    let url:NSURL = NSURL(string:"http://jsonplaceholder.typicode.com/comments")
    let request:NSURLRequest = NSURLRequest(URL:url)
    let queue:NSOperationQueue = NSOperationQueue()
    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in

        var stringData :NSString = NSString(data:data, encoding:NSUTF8StringEncoding)
        println(stringData)
        var err: NSError?

        var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary

        if(err?) {
            println("JSON Error (err!.localizedDescription)")
        }

        println("AsSynchronous\(jsonResult)")


        })

Upvotes: 3

Views: 4488

Answers (3)

Naishta
Naishta

Reputation: 12383

Modified the above answer to work for the latest Swift 2.1 version and type casted to NSArray as mentioned above, it works and processes the json data

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = NSURL(string: "http://jsonplaceholder.typicode.com/comments")

     //  or use this  http://jsonplaceholder.typicode.com/comments or this http://puppygifs.tumblr.com/api/read/json

        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in

        if error != nil {

                print(error)

            } else {

                do {

                    let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray

                // print(jsonResult)

                    print(jsonResult[0])

                } catch {
                    print("my error")
                }


            }

        })

        task.resume()

    }


}

Upvotes: 1

Connor
Connor

Reputation: 64674

As an addition to your own solution, you can use an if let statement to make your casting more concise.

if let jsonResult: AnyObject = NSJSONSerialization.JSONObjectWithData(data,options:nil,error: nil) {
    if let myDict = jsonResult as? NSDictionary {
        println("myDict:\(myDict)")
    }
    else if let myArray = jsonResult as? NSArray {
        println("myArray:\(myArray)")
    }
}

The if let statements in this example check if jsonResult can be cast to NSDictionary or NSArray and if they can set myDict/ myArray to a type cast version of jsonResult and enter the if body. If they can't be cast the code inside the if won't be executed. It's functionally the same as your solution, but a feels a bit more concise and cleaner to me.

Also, since you used the option NSJSONReadingOptions.MutableContainers you may want to cast as NSMutableArray and NSMutableDictionary.

Upvotes: 1

Marty Griffin
Marty Griffin

Reputation: 349

Changing

var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary

to

   if let jsonResult: AnyObject = NSJSONSerialization.JSONObjectWithData(data,options:nil,error: nil) {
                if jsonResult is NSDictionary {
                    var myDict: NSDictionary = jsonResult as NSDictionary
                    println("myDict:\(myDict)") 
                }
                else if jsonResult is NSArray {
                    var myArray: NSArray = jsonResult as NSArray
                    println("myArray:\(myArray)")

            }
        }

Got everything working. It was able to parse it as a NSArray and not a NSDictonary. As per code in a stack overflow chat, the code I have now will work for both JSON Arrays and JSON Objects that are returned by a Http request.

Upvotes: 4

Related Questions