Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store immutable arrays in a variable stored property in Swift?

I would like my class to have a stored property that can be assigned immutable arrays. If I do this:

class MyClass{
  var myItems:[String]
}

I can assign different arrays to my property, but the arrays will be mutable. If I do this:

class MyClass{
  let myItems:[String]
}

My array is immutable, but I can't ever change what's assigned to it. Is there any way to have my cake not mutate it too?

The best I've come up with is creating a wrapper around the array, then using that type for the property, like so:

class MyClass{
  struct ImmutableWrapper{
    let array:[String]
  }
  var myItems:ImmutableWrapper
}

…which is not exactly elegant.

like image 549
jemmons Avatar asked Jan 10 '15 03:01

jemmons


People also ask

Are arrays immutable in Swift?

If you assign an array, a set, or a dictionary to a constant, that collection is immutable, and its size and contents can't be changed.

Are arrays immutable?

Mutable is a type of variable that can be changed. In JavaScript, only objects and arrays are mutable, not primitive values.

What are stored properties in Swift?

Stored Properties A stored property is a property whose value is stored as part of the instance of a particular type. Stored properties can be either variable or constant. We can use var to create a variable stored property, and let to create a constant stored property.

Are arrays in JavaScript immutable?

In JavaScript, when you assign an existing array or object to a new variable, it does not create a new array or object with the same properties. Instead, it creates a reference to the original. It is not immutable.


1 Answers

I'd propose a slightly more concise solution than @matt's excellent answer, which again takes advantage of access modifiers. Instead of specifying a separate private variable, and exposing a public/internal computed variable that only provides a getter, you can let Swift do this for you by using private(set):

class MyClass {
    private(set) var myItems = [String]()
}

This specifies that the setter should be private to the current file scope. The getter retains the default access level (public/internal) so outside classes can still retrieve a copy of myItems (a copy since arrays are structs, so are passed by value*).

But you might then think you can just call a mutating method like append() on myItems, and in doing so modify it from outside the class (after all, we're getting var myItems, not let myItems). But Swift is smart enough to recognise that the array should be immutable from outside MyClass:

let object = MyClass()
object.myItems.append("change me")
//     ^       ~~~~~~
// Immutable value of type '[(String)]' only has mutating members named 'append'


So this achieves a very similar effect to the old Objective-C paradigm of maintaining a private NSMutableArray, and exposing a public readonly NSArray that creates an immutable copy of the private array when called. Much more elegant though, don't you think?


*In actual fact Swift is really smart with passing by value, and may not even make a copy of the array until the point it is modified, and even then may only keep track of the changes (internally). This means you can assign an array with many thousands of items without incurring a massive performance penalty.

like image 83
Stuart Avatar answered Oct 20 '22 21:10

Stuart