1

My PHP server-side returns a JSON like this:

[{"scan_status":"ok","visitorData":[{"visitorCompany":"xyl","visitorStreet":"street","visitorBranche":"health","visitorEmail":"[email protected]","lastmodified":"2014-12-15 14:18:55"}]}]

Now in Swift I would like to store this data, and for this I am trying to parse the data into Swift variables, however I got stuck.

do {
    //check wat we get back
    let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves )
    let vData = jsonData[0]["visitorData"]
    //let vCompany = vData["visitorCompany"]
    print("Test vData: \(vData)")
}

This prints

Test vData: Optional(( { visitorStreet = street; visitorPhone = 01606478; visitorCompany = xyl; visitorBranche = Sports; visitorEmail = "[email protected]"; lastmodified = "2014-12-15 14:18:55"; } ))

but when I try to get visitorCompany with

let vCompany = vData["visitorCompany"]

I get a compile error:

Cannot subscript a value of type 'AnyObject?!' with an index of type 'String'

BTW, why do we see the equals sign in swift i.e. visitorStreet = street?

1
  • I'd create and populate a custom class with the data returned from the PHP call... this way you can ensure you keep everything typed correctly. Commented Jan 4, 2016 at 12:44

3 Answers 3

2

This is because the compiler doesn't know the type of your decoded objects.

Help the compiler using casting with if let:

do {
    let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves )
    if let vData = jsonData[0]["visitorData"] as? [[String:AnyObject]] {
        if let vCompany = vData[0]["visitorCompany"] {
            print(vCompany)
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

let vData = jsonData[0]["visitorData"] populates vData with a generic AnyObject?, because Swift can't know what kind of objects PHP returns in the JSON.

You need to do a an optional cast to another dictionary before you can use vData like you want: jsonData[0]["visitorData"] as? [String:AnyObject].

And because a conditional cast returns an optional, it's best you do an optional binding to unwrap that optional, resulting in a code similar to this:

if let vData = jsonData[0]["visitorData"] as? [String:AnyObject] {
    //let vCompany = vData["visitorCompany"]
    print("Test vData: \(vData)")
}

Or even better, as jsonData can not be an array, or it could be an empty array (the server malfunctions and sends an invalid json for example), you can go even further with the validation:

if let items = jsonData as? [[String:AnyObject]], vData = items.first?["visitorData"] {
    //let vCompany = vData["visitorCompany"]
    print("Test vData: \(vData)")
}

items = jsonData as? [[String:AnyObject]] fails if jsonData is not an array, while vData = items.first?["visitorData"] fails if items.first is nil (optional chaining here), or if items.first doesn't have a visitorData key.

2 Comments

When i try your last suggestion i get a compile error ` Cannot subscript a value of type 'AnyObject'`
@alex I had a typo in my code, I corrected it meanwhile, the code should compile now. It should've read jsonData, not jsonData[0], as we need to test for jsonData
-1

Try with this:

let vData = jsonData[0]["visitorData"]![0] as! [String:AnyObject]

2 Comments

i can't add ![0] as! [String:AnyObject] to let vData = jsonData[0]["visitorData"]. I am getting a compile error Cannot subscript a value of type 'AnyObject?' with an index of type 'Int' with combo Eric D. answer ` let vData = jsonData[0]["visitorData"] as! [[String:AnyObject]] let vCompany = vData[0]["visitorCompany"]`
Forced unwrapping is considered code smell, and should be avoided as much as possible. Use optional binding instead

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.