0

What am I missing...?

elsewhere in my project there is code like this:

let allUsers = ["userName":["difficulty": 1, "highscore": 50],"userName2":["difficulty": 2, "highscore: 75]] defaults.setObject(allUsers, forKey: "allUsers") 

I want to change a value for one user in that array of users:

var allUsers = defaults.objectForKey("allUsers") as! [String:NSMutableDictionary] let changingUser = allUsers["userName"]! as NSMutableDictionary 

Neither of these will work:

changingUser.setObject(3, forKey: "difficulty") changingUser["difficulty"] = 3 

with the error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'

2
  • 5
    The objects returned from objectForKey will be immutable. You can't simply downcast an immutable dictionary to a mutable dictionary. You need to create a new NSMutableArray from the array returned from user defaults, modify that and then write it back. Commented Jun 3, 2016 at 21:58
  • You could also type your allUsers dictionary initially, such as let allUsers: [String:NSMutableDictionary] = .... Your cast to NSMutableDictionary would be correct at that point. Commented Jun 3, 2016 at 22:12

1 Answer 1

1

You can't just cast a NSDictionary to a NSMutableDictionary. You can initialize one by passing in an immutable counterpart, but unfortunately that isn't a deep mutable structure.

However, you can cast it to a Swift Dictionary and assign it to a var:

var allUsers = defaults.objectForKey("allUsers") as? [String: [String: Int]] ?? [:] allUsers2["userName"]?["difficulty"] = 3 

But note that Swift dictionaries are value objects and have value semantics. That is, if you do this in steps like so:

var allUsers = defaults.objectForKey("allUsers") as? [String: [String: Int]] ?? [:] changingUser = allUsers["userName"] changingUser?["difficulty"] = 3 //mutates a copy 

This will not work, since changingUser is a copy and while writing to it, does mutate it, it doesn't mutate the allUsers dictionary. So you have to either write the changed inner dict back into the outer dict, or do as I did above.

Sign up to request clarification or add additional context in comments.

2 Comments

So it checks whether allUsers is a [String: [String: Int]] and if not then [:] does what? just sets it to an array of AnyObjects? In my case, it will always be [String: [String: Int]]. Thank you!
No, the type system guarantees that a given variable (or constant) always has s consistent type. So the ?? part is a default value of the cast fails, that returns an empty dictionary, but still of type [String: [String: Int]]

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.