Xcode Swift: Could not cast value of type '__NSCFString' (0x102e8ac50) to 'NSDictionary' (0x102e8b8d0)

13,881

The error is clear, you are trying to convert a string to a dictionary. In the json which you posted the key "image", "image2" and "image3" are strings.

{
    "titulo": "Verox's 1002",
    "image": "http://wlodsgn.x10host.com/img/jeans/vrxjns1002/veroxjeans1002_front.jpg",
    "image2": "http://wlodsgn.x10host.com/img/jeans/vrxjns1002/veroxjeans1002_rightside.jpg",
    "image3": "http://wlodsgn.x10host.com/img/jeans/vrxjns1002/veroxjeans1002_front_b.jpg",
    "marca": "Verox",
    "color": "Azul",
    "tipo": "Jean",
    "ref": 1002
}

so the correct way the get those values is:

if let let imageURL = data["image"] as? String {
    self.imageUrl = imageURL
}

Avoid to use the explicit unwrap if you are not sure of the type of the object that you are try to casting.

UPDATE

The new error is of the same nature of the previous one, you are implicitly unwrapping a nil value that in this case is the closure completion. Your definition of the closure is completion: ((AnyObject) -> Void)! say to the compiler:

Don't worry when you will need to use this closure it will certainly has a non-nil value.

But instead you are passing a nil value here:

json.loadBbup(nil)

The implicit unwrapping is useful but you are using it in a wrong way. If you want that the completion closure can be nil you have to define it as optional in this way:

completion: ((AnyObject) -> Void)?

and the call it in this way:

completion?(jnslst)

Regarding the fact that the array contains only 3 values instead 20 there be a lot of reasons. I suggest to put a breakpoint in this line jnslst.append(jnsextrct) and watch the value of jnsextrct.

As general advices:

  • avoid to use the implicit unwrapping unless you're certain the variable as non-nil value.
  • use let instead var when you don't need to modify a variable; in this way you are certain that value never changes.
Share:
13,881
wlmrlsda
Author by

wlmrlsda

Updated on June 04, 2022

Comments

  • wlmrlsda
    wlmrlsda over 1 year

    I am trying to display my JSON information in the debug area but I am having a problem in which I have not found a solution.

    First of all, let me display the code I have so far:

    BBUpJSON.swift

    import Foundation
    
    class BBUpJSON {
    
        func loadBbup(completion: ((AnyObject) -> Void)!) {
    
            var urlString = "http://xxxdsgn.xxxhost.com/json/jnslst.json"
    
            let session = NSURLSession.sharedSession()
            let bbupUrl = NSURL(string: urlString)
    
            var task = session.dataTaskWithURL(bbupUrl!){
                (data, response, error) -> Void in
    
                if error != nil {
                    println(error.localizedDescription)
                } else {
    
                    var error : NSError?
    
                    var bbupData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as! NSArray
    
                    var jnslst = [JSONExtrct]()
    
                    for jnsextrct in bbupData{
                        let jnsextrct = JSONExtrct(data: jnsextrct as! NSDictionary)
                        jnslst.append(jnsextrct)
                    }
    
                    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
                    dispatch_async(dispatch_get_global_queue(priority, 0)) {
                        dispatch_async(dispatch_get_main_queue()) {
                            completion(jnslst)
                        }
                    }
    
                }
            }
    
            task.resume()
        }
    
    }
    

    JSONExtrct.swift

    import Foundation
    
    class JSONExtrct {
    
        var titulo : String!
        var imageUrl : String!
        var imageData : NSData?
        var imageUrl2 : String!
        var imageData2 : NSData?
        var imageUrl3 : String!
        var imageData3 : NSData?
        var marca : String!
        var color : String!
        var tipo : String!
        var ref : Int!
    
        init(data : NSDictionary) {
    
            self.titulo = getStringFromJSON(data, key: "titulo")
    
            let image = data["image"] as! NSDictionary
            self.imageUrl = getStringFromJSON(image, key: "image")
    
            let image2 = data["image2"] as! NSDictionary
            self.imageUrl2 = getStringFromJSON(image, key: "image2")
    
            let image3 = data["image3"] as! NSDictionary
            self.imageUrl3 = getStringFromJSON(image, key: "image3")
    
            self.marca = getStringFromJSON(data, key: "marca")
    
            self.color = getStringFromJSON(data, key: "color")
    
            self.tipo = getStringFromJSON(data, key: "tipo")
    
            self.ref = data["ref"] as! Int
    
        }
    
        func getStringFromJSON(data: NSDictionary, key: String) ->String{
    
            //let info : AnyObject? = data[key]
    
            if let info = data[key] as? String {
                return info
            }
    
            return ""
    
        }
    
    }
    

    ViewController.swift

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let json = BBUpJSON()
            json.loadBbup(nil)
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
    }
    

    So, when I run the app with a breakpoint in for jnsextrct in bbupData{ in the BBUpJSON.swift file, it shows in the left side of the debug area that I have 20 values. Everytime I run po bbupData in the right output of the debug area it just partially shows me 3 of the values out of the 20 I have in my JSON file. When I add another breakpoint in completion(jnslst) located at the end of the same swift file and press Continue program execution, I get the following error: Could not cast value of type '__NSCFString' (0x10c3c2c50) to 'NSDictionary' (0x10c3c38d0). It then redirects the error to let image = data["image"] as! NSDictionary that is located in my JSONExtrct.swift.

    This is the link to the json file that is trying to extract the information.

    -----UPDATE------

    Modified the values of the strings in JSONExtrct.swift to:

    if let imageUrl = data["image"] as? String {
                self.imageUrl = imageUrl
            }
    
            if let imageUrl2 = data["image2"] as? String {
                self.imageUrl2 = imageUrl2
            }
    
            if let imageUrl3 = data["image3"] as? String {
                self.imageUrl3 = imageUrl3
            }
    

    And added the same breakpoints to see the results in BBUpJSON.swift. From the breakpoint for jnsextrct in bbupData{, it still displays me the same 3 values out of the 20 I have (don't know if that should show that way) when I type po bbupData in the output area. When I Continue program execution to completion(jnslst), it doesn't show me the previous error but it shows me the following output result when typing po jnslst:

    (lldb) po jnslst
    [BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct, BonBonUp.JSONExtrct]
    

    When I continue and add one more breakpoint at the last bracket, it shows me the following error: fatal error: unexpectedly found nil while unwrapping an Optional value and it redirects the error to completion(jnslst) showing Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode=0x0)

    UPDATE 2

    Breakpoint Line 1

    Breakpoint Line 2

    It seems that it reads just one value through jnsextrct and returns it to jnslst. Then when I add the other breakpoint in the next screenshot it goes to the next value. When I type po jnslst in the second screenshot, it returns one [BonBonUp.JSONExtrct] instead of 20 of them like in the previous update.

    UPDATE 3

    Already added CollectionView to the Main.StoryBoard but I am getting the following errors when I try to upload JSON information (Not images yet) to each grid. Under JnsGridController.swift, in order to receive the information from my JSON files, I had to create a new method that would callback as didLoadJns. Here is the part of that code:

    jns = [JsonExtrct]()
            let json = JnsJson()
            json.loadJns(didLoadJns)
        }
    
        func didLoadJns(jns: [JsonExtrct]){
            self.jns = jns
            collectionView.reloadData()
    

    In json.loadJns(didLoadJns), didLoadJns use to be nil before I added the func method but I am getting the following error: Cannot invoke 'loadJns' with an argument list of type '(([JsonExtrct]) -> ())'

    Also in JnsJson.swift, since I added that callback function in JnsGridController its going to be receiving arrays from JsonExtrct instead of AnyObject so I changed the function method from func loadJns(completion: ((AnyObject) -> Void)!) { to func loadJns(completion: ((JsonExtrct) -> Void)!) { and now I am getting a similar error as the one in JnsGridController: Cannot invoke 'dispatch_async' with an argument list of type '(dispatch_queue_t!, () -> _)'