Adelmaer Adelmaer - 2 months ago 95
Swift Question

Problems with getting values out of nested dictionary in Swift 3 and Xcode 8

I parse JSON with this :

let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData as Data, options: .mutableContainers)


and get the following nested dictionary as a result

{ response = { GeoObjectCollection = { featureMember =
(
{ GeoObject = { Point = { pos = "40.275713 59.943413"; }; }; },
{ GeoObject = { Point = { pos = "40.273162 59.944292"; }; }; }
);
};
};
}


I'm trying to get the values of coordinates out of this dictionary and save them into new latutudeString and longitudeString variables

Until Xcode 8 GM it worked for me with this code:

if let jsonCoordinatesString: String = dictionary["response"]!!["GeoObjectCollection"]!!["featureMember"]!![0]["GeoObject"]!!["Point"]!!["pos"]!! as? String {

var latLongArray = jsonCoordinatesString.components(separatedBy: " ")

let latitudeString = latLongArray[1]
let longitudeString = latLongArray[0]

}


But since I've installed Xcode 8 GM i receive an error:


Type Any has no Subscript members


enter image description here

How to fix it it Swift 3 with Xcode 8 ? I've read that I can cast it but don't know exactly how to make it work with my nested dictionary in swift 3 with the latest Xcode. I've read can't resolve "Ambiguous use of subscript" but it really did not helped me in my case.

Answer

Your JSON data has this type in Swift:

[String: [String: [String: [[String: [String: [String: String]]]]]]]

I would avoid using such a too deeply nested type, but you can write something like this, if you dare use it:

enum MyError: Error {
    case invalidStructure
}
do {
    guard let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData as Data, options: .mutableContainers) as? [String: [String: [String: [[String: [String: [String: String]]]]]]] else {
        throw MyError.invalidStructure
    }
    if let jsonCoordinatesString: String = dictionary["response"]?["GeoObjectCollection"]?["featureMember"]?[0]["GeoObject"]?["Point"]?["pos"] {

        var latLongArray = jsonCoordinatesString.components(separatedBy: " ")

        let latitudeString = latLongArray[1]
        let longitudeString = latLongArray[0]

    }
} catch let error {
    print(error)
}

But you may be hiding some irrelevant members of the JSON data, which might break this as? conversion.

So, you can go step by step, in some cases "need to" in Swift 3, like this:

do {
    guard let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData as Data, options: .mutableContainers) as? [String: AnyObject] else {
        throw MyError.invalidStructure
    }
    if
        let response = dictionary["response"] as? [String: AnyObject],
        let geoObjectCollection = response["GeoObjectCollection"] as? [String: AnyObject],
        let featureMember = geoObjectCollection["featureMember"] as? [[String: AnyObject]],
        !featureMember.isEmpty,
        let geoObject = featureMember[0]["GeoObject"] as? [String: AnyObject],
        let point = geoObject["Point"] as? [String: AnyObject],
        let jsonCoordinatesString = point["pos"] as? String
    {
        var latLongArray = jsonCoordinatesString.components(separatedBy: " ")

        let latitudeString = latLongArray[1]
        let longitudeString = latLongArray[0]

    }
} catch let error {
    print(error)
}

(lets are mandatory for each Optional-bindings in Swift 3. And you can change all AnyObjects to Anys, if you prefer.)

Comments