Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot downcast from 'AnyObject' whilst parsing JSON array

Tags:

json

ios

swift

I'm in the process of trying to migrate an existing project to Swift, its more of a learning exercise but I think I've hit a problem which seems pretty basic and I think it may relate to my lack of understanding of AnyObject.

I've created a object that consists of a number of variables and is initialised as:

var customObject:MycustomObject = MYcustomObject()

I'm then using NSURLConnection to retrieve JSON data and this all appears to be working correctly as the jsonArray is being populated with data

jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSArray

I'm then looping through the array and attempting to parse the data and this is where I'm experiencing problems, the for loop defaults details to AnyObject.

for details:AnyObject in jsonArray {
    parseDetail(details as NSDictionary)
}

The problem relates to the integerValue, its generating an "Cannot downcast from 'AnyObject' to non-@objc...." error

func parseDetail(element: NSDictionary ) {
        self.customObject.stringValue      = element["id"] as String
        self.customObject.integerValue     = element["type"] as Integer
}

There also seems to be some differences in the way NSString & String are working, my understanding so far is that when using Swift I should be using the native types Sting, Float Integer etc etc. If I use NSString the stringValue displays correctly in the debug window but when using the type String I receive the following:

{
  core = {
    _baseAddress = Builtin.RawPointer = 0x0b227f49
    _countAndFlags = 1073741828
    _owner = Some {
      Some = (instance_type = Builtin.RawPointer = 0x0b227f40 -> 0x006d58f0 (void *)0x006d58c8:          __NSCFString)
    }
  }
}

Sorry this is a little long but is there any info regarding threading, this is my next challenge?

NSThread.detachNewThreadSelector(Selector: parseDetail(), toTarget: self, withObject: nil)
like image 790
Duncan Hill Avatar asked Jun 08 '14 22:06

Duncan Hill


1 Answers

Here's a little example that does the job. First, here's a model class for our purposes:

class CustomObject {
    var name: String = ""
    var age : Int    = 0

    init(json: Dictionary<String, AnyObject>) {
        name = json["name"] as NSString
        age  = (json["age" ] as NSNumber).integerValue
    }
}

As you can see, we still need to deal with NSNumbers and NSStrings since NSJSONSerialization is not yet updated to the Swift types.

Next, a little json.txt file residing in our project:

[{"name":"Alice","age":30},{"name":"Bob","age":40}]

And last, the parser, right in application launch for a quick test:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    var filePath  = NSBundle.mainBundle().pathForResource("json", ofType:"txt")
    var data      = NSData(contentsOfFile:filePath)
    var json      = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as Array<Dictionary<String, AnyObject>>

    var customObjects = json.map { dict in CustomObject(json: dict) }

    println(customObjects[0].name)
    println(customObjects[0].age)

    return true
}

This will print:

Alice
30
like image 141
Jean Le Moignan Avatar answered Nov 11 '22 06:11

Jean Le Moignan