Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a pretty complex data structure in my app, which I need to manipulate. I am trying to keep track of how many types of bugs a player has in thier garden. There are ten types of bugs, each with ten patterns, each pattern having ten colors. So there are 1000 unique bugs possible, and I want to track how many of each of these types the player has. The nested dictionary looks like:

var colorsDict: [String : Int]
var patternsDict: [String : Any] // [String : colorsDict]
var bugsDict: [String : Any] // [String : patternsDict]

I do not get any errors or complaints with this syntax.

When I want to increment the player's bug collection though, doing this:

bugs["ladybug"]["spotted"]["red"]++

I get this error: String is not convertible to 'DictionaryIndex< String, Any >' with the error's carrot under the first string.

Another similar post suggested using "as Any?" in the code, but the OP of that post only had a dictionary one deep so could do that easily with: dict["string"] as Any? ...

I am not sure how to do this with a multilevel dictionary. Any help would be appreciated.

When working with dictionaries you have to remember that a key might not exist in the dictionary. For this reason, dictionaries always return optionals. So each time you access the dictionary by key you have to unwrap at each level as follows:

bugsDict["ladybug"]!["spotted"]!["red"]!++

I presume you know about optionals, but just to be clear, use the exclamation mark if you are 100% sure the key exists in the dictionary, otherwise it's better to use the question mark:

bugsDict["ladybug"]?["spotted"]?["red"]?++

Addendum: This is the code I used for testing in playground:

var colorsDict = [String : Int]()
var patternsDict =  [String : [String : Int]] ()
var bugsDict = [String : [String : [String : Int]]] ()
colorsDict["red"] = 1
patternsDict["spotted"] = colorsDict
bugsDict["ladybug"] = patternsDict
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 1
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 2
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 3
bugsDict["ladybug"]!["spotted"]!["red"]! // Prints 4
                That's a great thought, and I have added the ! after each segment, but this doesn't solve my problem. I am still getting the error mentioned in my post: String is not convertible to DictionaryIndex<String, Any>
– zeeple
                Aug 24, 2014 at 23:31
                See the update in my answer and see if it helps. Never seen the error you're mentioning, using either ! and ?
– Antonio
                Aug 25, 2014 at 4:16
                Thanks Antonio!  I'll dig into this tonight and accept your answer once I have the issue resolve din my code. I'll also post if I still can't get it for some reason...
– zeeple
                Aug 26, 2014 at 16:17
                bugsDict["ladybug"]?["spotted"]?["red"]?++ <- This didn't work in Swift 2. But this worked -> if let spottedDict = bugsDict["ladybug"]?["spotted"], let red = spottedDict?["red"] {} 
– Varun Singh
                Oct 27, 2016 at 8:53
                @ViruMax Any does not have arithmetic operators, I think you have to make the expression more explicit, like bugsDict["ladybug"]!["spotted"]!["red"] = (bugsDict["ladybug"]!["spotted"]!["red"] as! Int) + 1
– Antonio
                Mar 29, 2019 at 9:06
var d = [ "ladybug" : [ "spotted" : [ "red" : 123 ] ] ] as [String:Any]
(d as NSDictionary).value(forKeyPath: "ladybug.spotted.red")

and it works, but this is probably the best way:

d["ladybug"]?["spotted"]?["red"]
                NSObject implements valueForKeyPath, so depending on what dict is you could cast to NSObject, if necessary. (NSDictionary implements objectForKey)
– nielsbot
                Jul 22, 2015 at 17:04
                dict.valueForKeyPath() or valueForKey() is available in Swift 3 ? I guess Apple has removed them.
– Nasir
                Dec 7, 2016 at 10:05

I tried a lot of solution but those didn't worked for me as i was missing type casting. So I used following code to get the boolValue from json, where json is a nested dictionary of type [String:Any].

let boolValue = ((json["level1"]
    as? [String: Any])?["level2"]
    as? [String: Any])?["boolValue"] as? Bool

My primary use case was reading ad-hoc values from a deep dictionary. None of the answers given worked for me in my Swift 3.1 project, so I went looking and found Ole Begemann's excellent extension for Swift dictionaries, with a detailed explanation on how it works.

I've made a Github gist with the Swift file I made for using it, and I welcome feedback.

To use it, you can add the Keypath.swift into your project, and then you can simply use a keyPath subscript syntax on any [String:Any] dictionary as follows.

Considering you have a JSON object like so:

"name":"John", "age":30, "cars": { "car1":"Ford", "car2":"BMW", "car3":"Fiat"

stored in a dictionary var dict:[String:Any]. You could use the following syntax to get to the various depths of the object.

if let name = data[keyPath:"name"] as? String{
    // name has "John"
if let age = data[keyPath:"age"] as? Int{
    // age has 30
if let car1 = data[keyPath:"cars.car1"] as? String{
    // car1 has "Ford"

Note that the extension supports writing into nested dictionaries as well, but I haven't yet used this.

I still haven't found a way to access arrays within dictionary objects using this, but it's a start! I'm looking for a JSON Pointer implementation for Swift but haven't found one, yet.

Have not tried this yet on Swift 4. Please post another comment or an answer if you get somewhere with this, thanks! – Dhiraj Gupta Jul 17, 2018 at 16:06

If it's only about retrieval (not manipulation) then here's a Dictionary extension for Swift 3 (code ready for pasting into Xcode playground) :

//extension
extension Dictionary where Key: Hashable, Value: Any {
    func getValue(forKeyPath components : Array<Any>) -> Any? {
        var comps = components;
        let key = comps.remove(at: 0)
        if let k = key as? Key {
            if(comps.count == 0) {
                return self[k]
            if let v = self[k] as? Dictionary<AnyHashable,Any> {
                return v.getValue(forKeyPath : comps)
        return nil
//read json
let json = "{\"a\":{\"b\":\"bla\"},\"val\":10}" //
if let parsed = try JSONSerialization.jsonObject(with: json.data(using: .utf8)!, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<AnyHashable,Any>
    parsed.getValue(forKeyPath: ["a","b"]) //-> "bla"
    parsed.getValue(forKeyPath: ["val"]) //-> 10
//dictionary with different key types
let test : Dictionary<AnyHashable,Any> = ["a" : ["b" : ["c" : "bla"]], 0 : [ 1 : [ 2 : "bla"]], "four" : [ 5 : "bla"]]
test.getValue(forKeyPath: ["a","b","c"]) //-> "bla"
test.getValue(forKeyPath: ["a","b"]) //-> ["c": "bla"]
test.getValue(forKeyPath: [0,1,2]) //-> "bla"
test.getValue(forKeyPath: ["four",5]) //-> "bla"
test.getValue(forKeyPath: ["a","b","d"]) //-> nil
//dictionary with strings as keys
let test2 = ["one" : [ "two" : "three"]]
test2.getValue(forKeyPath: ["one","two"]) //-> "three"
                I bet you could make this a variadic function, then you wouldn't need the array syntax...
– nielsbot
                Mar 20, 2017 at 20:54

Unfortunately none of these methods worked for me, so I built my own to use a simple string path like "element0.element1.element256.element1", etc. Hope this save a time for others. (just use a dots between name of elements in string)

Json example:

"control": { "type": "Button", "name": "Save", "ui": { "scale": 0.5, "padding": { "top": 24, "bottom": 32

Step 1, convert json String to Dictionary

static func convertToDictionary(text: String) -> [String: Any]? {
        if let data = text.data(using: .utf8) {
                return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
            } catch {
                print(error.localizedDescription)
        return nil

Step 2, helper to get a nested objects

//path example: "control.ui.scale"
    static func getDictValue(dict:[String: Any], path:String)->Any?{
        let arr = path.components(separatedBy: ".")
        if(arr.count == 1){
            return dict[String(arr[0])]
        else if (arr.count > 1){
            let p = arr[1...arr.count-1].joined(separator: ".")
            let d = dict[String(arr[0])] as? [String: Any]
            if (d != nil){
                return getDictValue(dict:d!, path:p)
        return nil

Step 3, use helper

let controlScale = getDictValue(dict:dict, path: "control.ui.scale") as! Double?
print(controlScale)
let controlName = getDictValue(dict:dict, path: "control.name") as! String?
print(controlName)

Returns

The Swift 4 default: subscript for Dictionaries makes makes updating values in nested Dictionaries much more concise.

Get and Set a default value rather than dealing with optionals:

var dict = [String : [String : String]]()
dict["deep", default: [:]]["nested"] = "dictionary"
print(dict)
// ["deep": ["nested": "dictionary"]]

https://swift.org/blog/dictionary-and-set-improvements/

/// - Description /// - The function will return a value on given keypath /// - if Dictionary is ["team": ["name": "KNR"]] the to fetch team name pass keypath: team.name /// - If you will pass "team" in keypath it will return team object /// - Parameter keyPath: keys joined using '.' such as "key1.key2.key3" func valueForKeyPath <T> (_ keyPath: String) -> T? { let array = keyPath.components(separatedBy: ".") return value(array, self) as? T /// - Description:" /// - The function will return a value on given keypath. It keep calling recursively until reach to the keypath. Here are few sample: /// - if Dictionary is ["team": ["name": "KNR"]] the to fetch team name pass keypath: team.name /// - If you will pass "team" in keypath it will return team object /// - Parameters: /// - keys: array of keys in a keypath /// - dictionary: The dictionary in which value need to find private func value(_ keys: [String], _ dictionary: Any?) -> Any? { guard let dictionary = dictionary as? [String: Any], !keys.isEmpty else { return nil if keys.count == 1 { return dictionary[keys[0]] return value(Array(keys.suffix(keys.count - 1)), dictionary[keys[0]])

Usage:

let dictionary = ["values" : ["intValue": 3]]
let value: Int = dictionary.valueForKeyPath("values.intValue")
if let car = data["cars"] as? [String:AnyObject],
    let car1 = car["car1"] as? String {
    // car1 has "Ford"

Yet another Dictionary extension

public extension Dictionary where Key: Hashable, Value: Any {
    subscript(keyPath path: String) -> Value? {
        self[keyPath: path.components(separatedBy: ".").compactMap { $0 as? Key }]
    private subscript(keyPath keys: [Key]) -> Value? {
        var keys = keys
        switch keys.first {
        case .some(let key) where keys.count == 1:
            return self[key]
        case .some(let key) where keys.count > 1:
            keys.removeFirst()
            return (self[key] as? Dictionary<Key, Value>)?[keyPath: keys]
        default:
            return nil

Test code:

let dict: [String: Any] = [
    "user": [
        "name": "Giorgio",
        "surname": "Baldazzi"
let keyPath = "user.name"
print(String(describing: dict[keyPath: keyPath]))

Yet another approach using various overloaded Dictionary subscript implementations:

let dict = makeDictionary(fromJSONString:
            "control": {
                "type": "Button",
                "name": "Save",
                "ui": {
                    "scale": 0.5,
                    "padding": {
                        "top": 24,
                        "bottom": 32
        """)!
dict[Int.self, ["control", "ui", "padding", "top"]] // 1
dict[Int.self, "control", "ui", "padding", "top"]   // 2
dict[Int.self, "control.ui.padding.top"]        // 3

And the actual implementations:

extension Dictionary {
    subscript<T>(_ type: T.Type, _ pathKeys: [Key]) -> T? {
        precondition(pathKeys.count > 0)
        if pathKeys.count == 1 {
            return self[pathKeys[0]] as? T
    // Drill down to the innermost dictionary accessible through next-to-last key
        var dict: [Key: Value]? = self
        for currentKey in pathKeys.dropLast() {
            dict = dict?[currentKey] as? [Key: Value]
            if dict == nil {
                return nil
        return dict?[pathKeys.last!] as? T
    // 2. Calls 1
    subscript<T>(_ type: T.Type, _ pathKeys: Key...) -> T? {
        return self[type, pathKeys]
extension Dictionary where Key == String {
    // 3. Calls 1
    subscript<T>(_ type: T.Type, _ keyPath: String) -> T? {
        return self[type, keyPath.components(separatedBy: ".")]
func makeDictionary(fromJSONString jsonString: String) -> [String: Any]? {
    guard let data = jsonString.data(using: .utf8)
        else { return nil}
    let ret = try? JSONSerialization.jsonObject(with: data, options: [])
    return ret as? [String: Any]
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.