How make a var modifiable but not assignable?

Suppose I have the following class:

class Some {
  var list = [String]()
}

// In other places, I want to append to the list
someInstance.list.append("new string")
// ...but I do not want to re-assign the list itself:
someInstance.list = [String]()

What is the exact syntax for declaring list?

Answered by DTS Engineer in 766887022

There's no syntax that does exactly what you're asking for, because Array is a value type, and so modifying the whole value and appending to the elements are both the same kind of modification. (With a reference type, replacing the reference and appending to the array are semantically different things.)

The simplest way to do this is to:

  1. Make the property privately settable:

    private(set) var list = [String]()

  2. Add a function on the Some type to append elements:

   func appendList(_ newElement: String) {
       list.append(newElement)
   }
   ...
   someInstance.appendList("new string")

This is the easiest approach if the number of ways of modifying the array are very limited. If you want to provide a lot of other mutating functions, then more complex approaches are possible.

Could you clarify the use case ?

  • You want to be able to append
  • Do you want to be able to remove as well ?

If so, if you empties the list and append new elements, that's equivalent to assigning a new list.

But if you only want to add items, you can use didSet:

class Some {
    var list : [String] = [] {
        didSet {
            if list.count <= oldValue.count { // <<--Will FORBID replacement
                list = oldValue
            }
        }
    }
    
}

let someInstance = Some()
someInstance.list.append("Item 1")
print("added 1", someInstance.list)
someInstance.list.append("Item 2")
print("added 2", someInstance.list)
someInstance.list.removeFirst()
print("Try remove 1", someInstance.list)
someInstance.list = []
print("Try remove all", someInstance.list)
someInstance.list[0] = "New item 1"
print("replaced 1", someInstance.list)

You get:

added 1 ["Item 1"]
added 2 ["Item 1", "Item 2"]
Try remove 1 ["Item 1", "Item 2"]
Try remove all ["Item 1", "Item 2"]
replaced 1 ["Item 1", "Item 2"]

If you want to authorise replacement:

class Some {
    var list : [String] = [] {
        didSet {
            if list.count < oldValue.count { // <<-- Will ALLOW replacement
                list = oldValue
            }
        }
    }
    
}

let someInstance = Some()
someInstance.list.append("Item 1")
print("added 1", someInstance.list)
someInstance.list.append("Item 2")
print("added 2", someInstance.list)
someInstance.list.removeFirst()
print("Try remove 1", someInstance.list)
someInstance.list = []
print("Try remove all", someInstance.list)
someInstance.list[0] = "New item 1"
print("replaced 1", someInstance.list)

Now you get:

added 1 ["Item 1"]
added 2 ["Item 1", "Item 2"]
Try remove 1 ["Item 1", "Item 2"]
Try remove all ["Item 1", "Item 2"]
replaced 1 ["New item 1", "Item 2"]
Accepted Answer

There's no syntax that does exactly what you're asking for, because Array is a value type, and so modifying the whole value and appending to the elements are both the same kind of modification. (With a reference type, replacing the reference and appending to the array are semantically different things.)

The simplest way to do this is to:

  1. Make the property privately settable:

    private(set) var list = [String]()

  2. Add a function on the Some type to append elements:

   func appendList(_ newElement: String) {
       list.append(newElement)
   }
   ...
   someInstance.appendList("new string")

This is the easiest approach if the number of ways of modifying the array are very limited. If you want to provide a lot of other mutating functions, then more complex approaches are possible.

How make a var modifiable but not assignable?
 
 
Q