Search code examples
macosswiftdictionaryxcode6osx-yosemite

How does one assign values to nested [NSObject: AnyObject] dictionaries?


I would like to have a Dictionary of Dictionaries. I'm coming from mostly a Python background, where I can simply allocate and assign a top-level key to be a new dictionary, then use a reference to that dictionary to update it no matter how deeply it is nested.

I am trying to do the same thing in Swift. I am using [NSObject: AnyObject] because it must be bridgeable to NSDictionary so I can store it in NSUserDefaults.

Here is an example Playground:

import Cocoa

var foo = [NSObject: AnyObject]()
foo["aa_key"] = "aa_value"
println(foo)

foo["bb_key"] = [NSObject: AnyObject]()
println(foo)

if var bar = foo["bb_key"] as? [NSObject: AnyObject] {
    bar["cc_key"] = "cc_value"
    println("bar: \(bar)")
} else {
    println("Could not get bb dictionary")
}
// bb_key is still an _empty_ dictionary in foo!
println("foo: \(foo)")

The resulting output in the console (not the playground) is:

[aa_key: aa_value]
[aa_key: aa_value, bb_key: { }]
bar: [cc_key: cc_value]
foo: [aa_key: aa_value, bb_key: { }]

I can see that in the section where the Optional result of the downcast is unwrapped into bar, that assignments to the bar Dictionary are correct. However, when I later print foo, the changes have not made it.

It appears that bar is not a proper reference to the dictionary that is stored in foo["bb_key"]. Why not?


Solution

  • In Swift all collection types are value types unlike reference types in Objective-C, that means the object in bar is not the same object as in foo["bb_key"]

    You have to reassign / update the value in foo["bb_key"]

    var foo = [NSObject: AnyObject]()
    foo["aa_key"] = "aa_value"
    println(foo)
    
    foo["bb_key"] = [NSObject: AnyObject]()
    println(foo)
    
    if var bar = foo["bb_key"] as? [NSObject: AnyObject] {
      bar["cc_key"] = "cc_value"
      foo.updateValue(bar, forKey: "bb_key")
      println("bar: \(bar)")
    } else {
      println("Could not get bb dictionary")
    }
    // bb_key is still an _empty_ dictionary in foo!
    println("foo: \(foo)")