forEach results in $0 is immutable error
You have an array of items that implement a protocol. If you don't tell Swift that this is an AnyObject
protocol (or class
protocol in earlier versions of Swift), it will assume that it can be implemented by a struct
.
In a forEach
loop, you will essentially have a let
variable (called $0
by default) assigned to each value in the array in turn. If you have an array of objects (instances of a class
), then you can modify properties of that item. If you have an array of struct
items, then those will be immutable. If you have an array of items that implement a protocol, then Swift will treat them as the more restrictive struct
unless you add : AnyObject
(or : class
in earlier versions of Swift) to your protocol definition.
For example:
protocol Xyzzy: AnyObject {
var prop: Int? { get set }
}
class Fred: Xyzzy, CustomStringConvertible {
var description: String { String(describing: prop) }
var prop: Int? = 17
}
let objects: [Xyzzy] = [Fred(), Fred(), Fred()]
print(objects) // [Optional(17), Optional(17), Optional(17)]
objects.forEach { $0.prop = nil }
print(objects) // [nil, nil, nil]
Your workaround:
protocols.forEach
{
var protocol = $0
protocol.prop = nil
}
works for class
objects because it creates a new var
pointer to the object, and then that allows you to modify the object. Note, this workaround only works for instances of classes. If your protocol is implemented by a struct
, then the new var
will be a new copy of the struct
item, and the original ones in the array will not be changed.
How to change Array
of Structs
for every element:
itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }
for specific element:
itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
.forEach { itemsArray[$0].someValue = newValue }