28

I'm working on a swift project and I have a couple of arrays. In one of my arrays, I do not want the client to be able to mutate it without using one of my specially-defined methods. On the other hand, I want the getter to be accessible. My questions comes up regarding append and setting properties.

Question 1: Does private(set) stop clients from calling array.append?

On another array I want to see if it has been changed.

Question 2: If I add a property observer onto the array using didSet , then is the didSet called when an element is appended to the array?

mfaani
  • 33,269
  • 19
  • 164
  • 293
raoul
  • 463
  • 1
  • 6
  • 10

3 Answers3

45

Question 1: Does private(set) stop clients from calling array.append?

Yes it does.

Question 2: If I add a property observer onto the array using didSet , then is it called when an element is appended to the array?

Yes, didSet is called when append() is called on it.

mfaani
  • 33,269
  • 19
  • 164
  • 293
trevorj
  • 2,029
  • 1
  • 16
  • 11
  • 1
    Do you know if this is documented anywhere? – ndmeiri Sep 15 '15 at 22:17
  • 11
    @onmyway133 I was not asking if the answer has been tested. I understand that I can test it myself. I was asking if the documentation mentions the answer anywhere. If it is mentioned officially, adding a link to that document might be helpful for future readers who would like more details on why this works. – ndmeiri May 11 '16 at 10:44
6

The answers to your questions are easy to understand when you realize that arrays in Swift are effectively passed by value. I say effectively because they behave as though they are copied when they are passed, but there is some clever magic under the hood to optimize things and avoid actually needlessly duplicating elements.

The didSet handler is called when a property value changes, which in Swift includes arrays. So append()ing to an array in Swift is actually analogous to a += on an integer: the array is first read, then a new array is created with the appended value, and then that new array is written back to the property. So you can see it will definitely call didSet if you call append() on an array property, and similarly, by making set private, external users won't be able to call append() as they won't be able to write the new value back to the array.

devios1
  • 36,899
  • 45
  • 162
  • 260
  • so in theory **if** array was a reference type...appending to it wouldn't cause a `didSet` callback? – mfaani Apr 09 '18 at 20:24
  • @Honey that’s correct. Modifications made to a mutable reference type are made “inside” the object and do not affect references. On the other hand since value types are immutable (changing anything creates a whole new struct), it is the entire value that is changing. – devios1 Apr 11 '18 at 11:32
  • Is this still true in today's Swift? I can't seem to reproduce this in Xcode 11.4.1, aka Swift 5.2. – Tony Topper Apr 17 '20 at 21:58
  • @TonyTopper Yes still true and pretty fundamental to how Swift works. – Robin Stewart Sep 28 '21 at 04:54
2
  1. Question 1 Yes, because mutating function calls change the stored value, thus private(set) does prohibit calls to mutating functions.
  2. Question 2 Yes, for the same reason, observers are triggered.

There is nothing specific to arrays here, this is a consequence of arrays being structs and append being a mutating member. Calling append is very similar to affecting a new value to the property. This is not super explicit, but there is more information in the doc for mutating members and stored properties that backup the fact that mutating is properly handled by the language.

Alix Mougenot
  • 21
  • 1
  • 1