Search code examples
arraysswiftstructupdatingkeypaths

How to update an array with WritableKeyPath in Swift 4.0


In Swift 4.0, I have an array of structs. Is there a way to use keyPaths to update all items in the array without using manually iterating like map or forEach? Something similar to objc [people makeObjectsPerformSelector: @selector(setName:) withObject: @"updated"];

struct Person {
    var name: String? = "Empty"
}
var people = [Person(), Person()]

//This only updates one person:
people[keyPath: \[Person].[0].name] = "single update"

//I'm looking to accomplish something like this without a map
let updatedPeople = people.map { (person: Person) -> Person in
    var copy = person
    copy[keyPath: \Person.name] = "updated"
    return copy
}

something like people[keyPath: \[People].all.name] = "update all without manually iterating"


Solution

  • Mutating into a member of an array requires an l-value. Swift's mechanism for l-values is the subscript, so we can use that:

    for i in people.indices {
        people[i][keyPath: \Person.name] = updated
        // or more simply, just:
        // people[i].name = "updated"
    
        // This even works too, but I can't see any reason why it would be desirable
        // over the other 2 approaches:
        // people[keyPath: \[Person].[i].name] = "update"
    }
    

    You could also use forEach, but I generally only recommend that over for in cases where you have an existing closure/function to pass in which has type (Index) -> Void:

    // meh
    people.indices.forEach {
        people[$0][keyPath: \Person.name] = "updated"
    }