swiftswift-keypath

Best way to copy set of parameters from one object to another


As example I have struct/class:

struct Pet {
   var id: String
   var name: String
   var age: Int
   var type: PetType
}

enum PetType: Int {
   case cat
   case dog
}

Is it possible to write extension to copy SET OF PARAMETERS from one object to another?

Sth like:

var pet1 = Pet(id: "MyCat1", name: "Cookie", age: 4, type: .cat)
let pet2 = Pet(id: "randomCatId", name: "Socks", age: 3, type: .dog)


pet1.copy(from: pet2, valuesOfKeys: [\.name, \.age] )
// after this pet1 will be:
// ("MyCat1", "Socks", 3, .cat) 
// instead of 
// ("MyCat1", "Cookie", 4, .cat)

if it is not possible, what is the best way copy from one object to another large set of parameters?


Solution

  • This can be achieved with parameter packs.

    First we write a method that copies one key path only:

    func copy<T, Value>(from src: T, to dest: inout T, keyPath: WritableKeyPath<T, Value>) {
        dest[keyPath: keyPath] = src[keyPath: keyPath]
    }
    

    Then, we can write a method that takes a tuple of key paths, all of them can be key paths to different types of values, and repeatedly call the above method.

    func copy<T, each Value>(from src: T, to dest: inout T, keyPaths: (repeat WritableKeyPath<T, each Value>)) {
        repeat copy(from: src, to: &dest, keyPath: each keyPaths)
    }
    

    This means that you should use a tuple instead of an array literal when calling the method:

    copy(from: pet2, to: &pet1, keyPaths: (\.name, \.age))