meaning-matters meaning-matters - 2 months ago 68
Swift Question

forEach results in $0 is immutable error

protocols.forEach { $0.prop = nil }


results in:

Cannot assign to property: '$0' is immutable


I worked around this with:

protocols.forEach
{
var protocol = $0

protocol.prop = nil
}


But why is the compiler okay with this? I would expect it could figure this out.

Answer

You have an array of items that implement a protocol. If you don't tell Swift that this is a class protocol, 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 : class to your protocol definition.

For example:

protocol Xyzzy: class {
    var prop: Int? { get set }
}

class Fred: Xyzzy, CustomStringConvertible {
    var description: String { return "\(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.

Comments