As of Swift 5.9 with the introduction of Variadic Types, we can tweak @arutyun-enfendzhyan's answer to allow multiple changes at once:
Our base struct
struct Test {
// If the values can change, they should be declared as vars
var string = String()
var int = Int()
var double = Double()
}
Our helper
struct KeyValuePair<Model, Value> {
let key: WritableKeyPath<Model, Value>
let value: Value
func update(_ model: inout Model) {
model[keyPath: key] = value
}
}
The implementation
extension Test {
func copy<each Value>(
with pair: repeat KeyValuePair<Self, each Value>
) -> Self {
var clone = self
repeat (each pair).update(&clone)
return clone
}
}
let test = Test()
test.copy(with:
.init(key: \.string, value: "string"),
.init(key: \.int, value: 10),
.init(key: \.double, value: 20)
)
>> Test(string: "string", int: 10, double: 20.0)
There are other options too:
extension Test {
func copy<each Value>(
keys: repeat WritableKeyPath<Self, each Value>,
values: repeat each Value
) -> Self {
var clone = self
repeat KeyValuePair(key: each keys, value: each values).update(model: &clone)
return clone
}
}
let test = Test()
test.copy(keys: \.string, \.int, \.double, values: "string", 10, 20.0)
>> Test(string: "string", int: 10, double: 20.0)
extension Test {
func copy<each Value>(
with tuple: repeat (key: WritableKeyPath<Self, each Value>, value: each Value)
) -> Self {
var clone = self
repeat KeyValuePair(key: (each tuple).key, value: (each tuple).value).update(model: &clone)
return clone
}
}
let test = Test()
test.copy(with: (\.string, "string"), (\.int, 10), (\.double, 20.0))
>> Test(string: "string", int: 10, double: 20.0)
Now about the Changeable
protocol...
It would be awesome if we could add all of this to the protocol the same way @arutyun-enfendzhyan did. But there seems to be a limitation with variadic types and protocol extensions.
We can see the error as follows:
protocol Changeable {}
extension Changeable {
func copy<each Value>(
with pair: repeat KeyValuePair<Self, each Value>
) -> Self {
var clone = self
repeat (each pair).update(&clone) // Type of expression is ambiguous without more context
return clone
}
}
Which is a shame, because we would need to copy and paste some code instead of reusing it with a simple protocol.
If anyone has other ideas on how to make use of protocols here, feel free to add a comment!
Update
As of the official release of Swift 5.9 and Xcode 15, this seems to be working within an xcode project, but still crashes my playground.
Full code:
import Foundation
protocol Changeable {}
extension Changeable {
func copy<each Value>(
with pair: repeat KeyValuePair<Self, each Value>
) -> Self {
var clone = self
repeat (each pair).update(&clone)
return clone
}
}
struct KeyValuePair<Model, Value> {
let key: WritableKeyPath<Model, Value>
let value: Value
func update(_ model: inout Model) {
model[keyPath: key] = value
}
}
struct Test: Changeable {
var string = String()
var int = Int()
var double = Double()
}
let test = Test()
test.copy(with:
.init(key: \.double, value: 1),
.init(key: \.int, value: 2),
.init(key: \.string, value: "3")
)
>> Test(string: "3", int: 2, double: 1.0)
b is immutable
contradictshow would I change, say, one property on the struct
. Unless you're looking for a way to initialize a new struct with the properties of the previous one except some of them? – CorenaA
need to belet
instead ofvar
? – Romoval jack = User(name = "Jack", age = 1) val olderJack = jack.copy(age = 2)
– ChemotherapyDataClass
types (Kotlin's version of a struct) and that function has optional parameters for each property. – Chemotherapy