I recently came across this question on Twitter:
This is an interesting question. As a developer coming from Objective-C and the Cocoa frameworks, we’re used to distinguishing between
NSMutableArray. It’s generally bad practice to expose an
NSMutableArray as a public property on an object.
The problem is that another part of the app could modify the contents of the
pizzaMenu array without the knowledge of the
Pizzeria object. The
Pizzeria still points to the same object it always has; from its perspective, nothing has changed. This is bad.
Instead, a better practice is to only expose an immutable
NSArray. This way, whenever any change happens, the setter method will be called. The
Pizzeria is always aware of what’s happening.
In Swift, the
Array type is built into the language. Though you can still use
NSMutableArray, it’s often not necessary; the native Array type is plenty capable.
There are two types of objects in Swift: value types and reference types. Reference types are like traditional Objective-C objects—multiple variables can reference the same object. This was the problem in the examples above above: both the
Pizzeria object and external code referenced the same object, so we had to expose an
NSArray that does not have APIs for changing its contents.
Value types are identified by their value. They also exist in Objective-C, but only for C types (structs and primitives). If two
CGRect variables have the same value, it simply means that the two rects contain the same data. A change to one rect will never change the value of another rect stored in a separate variable.
While Objective-C requires that all objects be reference types, Swift objects can be reference or value types. This means that Swift
structs can have methods just like
classes can. Swift’s built-in collections (
Dictionary) are value types.
Because they are value types, two variables can never reference the “same” array. Every time an array is assigned to a variable, it gets a new, separate copy1
which cannot be affected by any other part of the code.
This means there is no need for a separate mutable collection type in Swift. Mutating a value type is always safe because changing a struct only affects the values of that specific variable. That what it means to be a value type. Modifying a Swift
Array never affects any other variable, anywhere.
Let’s see how this affects our pizzeria example.
Note that even though
pizzeria.pizzaMenu is a mutable
var property, modifying the array we get back from the pizzeria has no effect on the pizzeria’s copy of the data. They are separate arrays, separate values, and we can’t change their copy unless we go through the setter.
In fact, this holds true even if we avoid assigning the array to an intermediate variable! In Objective-C, you simply cannot modify a value type held by another object. Many iOS developers have discovered this, to their frustration:
But in Swift, not only does this work, but it guarantees that the setter will be called! Whenever you call a mutating2
method on a value type, the end result is assigned back into the receiving variable (or expression). So calling
causes a new value to be constructed, which is then assigned back into the
Because Swift arrays and dictionaries can never be shared, there is no distinction between mutating an existing collection and re-assigning a new collection. The behavior of the code is exactly the same. In either case, the owner’s setter method is called whenever the array is modified.
So to answer the original question, there is no syntax to specify a variable that holds an immutable array because there is nothing that such syntax would add. Swift addresses the issues that made
NSMutableArray necessary in the first place. If you need a shared array, you can still use the Cocoa types. In every other case, Swift’s solution is safer, simpler, and more concise.
: Note that the implementation is lazy, only performing an actual copy when really necessary. ↩
: Methods on structs and enums in Swift must be labeled
mutating if they’re going to modify any data. Otherwise, they cannot be called on a value stored in a
let variable. ↩