var dict = [1: "abc", 2: "cde"]) and add entries with dict[3] = "efg". If an API forces NSDictionary on you, copy it into an NSMutableDictionary, mutate that, then bridge back.Why this happens
NSDictionary is an immutable Foundation collection. Once it exists, its contents are fixed — there is no append, no insert, and its subscript is read-only. So this code does not compile:
var dict: NSDictionary = [1: "abc", 2: "cde"]
dict[3] = "efg" // Cannot assign through subscript: subscript is get-only
If you declared it with let instead of var, the diagnostic is slightly different — Cannot assign through subscript: 'dict' is a 'let' constant — but the root cause is the same: you are trying to write through a read-only interface.
The second half of the problem is that NSDictionary is untyped from Swift’s point of view. Its keys and values are Any, so the compiler cannot stop you from mixing an Int key with a String key in the same dictionary. Native Dictionary<Key, Value> is generic, checked at compile time, and mutable when declared with var.
How to fix it
1. Use a native Swift Dictionary (the right fix)
Drop the : NSDictionary annotation and let Swift infer the type from the literal. The literal [1: "abc", 2: "cde"] becomes Dictionary<Int, String>, which is mutable and type-safe.
import Foundation
var dict = [1: "abc", 2: "cde"] // [Int: String]
dict[3] = "efg" // add a new pair
dict[2] = "changed" // overwrite an existing key
print(dict) // [1: "abc", 2: "changed", 3: "efg"]
Subscript assignment does both jobs: it inserts when the key is absent and replaces when it is present. That is the dictionary equivalent of “append”, and it is what most people actually want.
If you need the old value back, use updateValue(_:forKey:), which returns the previous value or nil:
let previous = dict.updateValue("xyz", forKey: 4) // nil, key was new
When you build the dictionary in stages and the type cannot be inferred, annotate it explicitly:
var scores: [String: Int] = [:]
scores["ada"] = 10
2. If you genuinely need NSDictionary, go through NSMutableDictionary
Sometimes a framework signature or an Objective-C API hands you an NSDictionary and you must keep that type. In that case, copy it into a mutable variant, change that, and copy back.
import Foundation
let original: NSDictionary = [1: "abc", 2: "cde"]
let mutable = original.mutableCopy() as! NSMutableDictionary
mutable[3] = "efg"
let updated = mutable.copy() as! NSDictionary
NSMutableDictionary(dictionary:) does the same job with a typed initializer:
let mutable = NSMutableDictionary(dictionary: original)
mutable[3] = "efg"
Two things to keep in mind. First, NSMutableDictionary keys must be objects that conform to NSCopying; Swift Int and String bridge to NSNumber and NSString, so simple literals work fine. Second, you are back to Any keys and values, so nothing stops a typo from producing a dictionary with mixed key types.
3. Bridge at the boundary, not in the middle
A Swift Dictionary bridges to NSDictionary automatically, so you rarely need a cast at all. Pass it straight to a function that expects NSDictionary:
func takesNSDictionary(_ d: NSDictionary) { /* ... */ }
var dict = [1: "abc", 2: "cde"]
dict[3] = "efg"
takesNSDictionary(dict) // bridges implicitly
let nsVersion = dict as NSDictionary // explicit, when a stored property needs it
Keep the mutable work in Swift types and convert only where the API boundary demands it. That way the compiler keeps checking your keys and values for the whole lifetime of the data.
4. Merging one dictionary into another
If you are adding several pairs at once, merge is clearer than a loop:
var dict = [1: "abc", 2: "cde"]
let extra = [3: "efg", 4: "hij"]
dict.merge(extra) { current, new in new }
The closure decides what happens on a key collision — return new to overwrite, or current to keep the existing value.
5. Counting and reading back
A common follow-up question is why the order looks random. Dictionary and NSDictionary are unordered; iteration order is not part of the contract. Sort the keys when you need a stable sequence:
for key in dict.keys.sorted() {
print(key, dict[key]!)
}
How to prevent it
- Default to
Dictionary/[Key: Value]in Swift code; reach forNSDictionaryonly when an API signature forces it. - Declare the variable with
varif you plan to add or change entries —letdictionaries are fixed. - Annotate the type (
[String: Int] = [:]) when creating an empty dictionary, otherwise the compiler has nothing to infer from. - Do the mutation in Swift and bridge to
NSDictionaryonly at the call site. - Remember that assigning to an existing key replaces the value; use
updateValue(_:forKey:)when you need the old one.
FAQ
Why does dict["key"] = "value" fail on my NSDictionary?
Because NSDictionary is immutable and its subscript has no setter. The same line compiles on NSMutableDictionary, which is the mutable subclass — but you lose the generic type checking that a Swift Dictionary gives you.
Can I append to a dictionary the way I append to an array?
No. Arrays are ordered and allow duplicates, so they have append. Dictionaries are keyed and unique per key, so you assign with a subscript or call updateValue(_:forKey:). Assigning to a key that already exists overwrites it rather than adding a second entry.
Does converting a Swift Dictionary to NSDictionary copy the data?
Bridging is handled for you and both types have value semantics, so treat the conversion as producing an independent value. Mutating the Swift dictionary afterwards does not change an NSDictionary you already created from it.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
