
Il arrive souvent que nous devions copier un objet, en changeant certaines de ses propriétés, mais en gardant le reste inchangé. Il existe une fonction pour cette tâche copy().Ceci est un extrait de la description de la méthode copy()de la documentation Kotlin . Dans notre langue Swift native, cela signifie quelque chose comme ceci:
struct User {
let id: Int
let name: String
let age: Int
}
let steve = User(id: 1, name: "Steve", age: 21)
// , `name` `age`
let steveJobs = steve.changing { newUser in
newUser.name = "Steve Jobs"
newUser.age = 41
}
Ça a l'air délicieux, n'est-ce pas?
, Swift " ". .
, var let?
struct User {
let id: Int
var name: String
var age: Int
}
let steve = User(id: 1, name: "Steve", age: 21)
...
var steveJobs = steve
steveJobs.name = "Steve Jobs"
steveJobs.age = 41
:
- , , , - .
- "". ,
willSetdidSet. - , .
, — , :
// , `name`
let steveJobs = User(
id: steve.id,
name: "Steve Jobs",
age: steve.age
)
, , . - , .
, , "" , .
:
- , .
-
Changeable-, , . - , .

, , . Key-Path , Key-Path Dynamic Member Lookup Swift 5.1 .
, generic-:
@dynamicMemberLookup
struct ChangeableWrapper<Wrapped> {
private let wrapped: Wrapped
private var changes: [PartialKeyPath<Wrapped>: Any] = [:]
init(_ wrapped: Wrapped) {
self.wrapped = wrapped
}
subscript<T>(dynamicMember keyPath: KeyPath<Wrapped, T>) -> T {
get {
changes[keyPath].flatMap { $0 as? T } ?? wrapped[keyPath: keyPath]
}
set {
changes[keyPath] = newValue
}
}
}
, KeyPath. , , . .
changes[keyPath] as? T,T.nil, , . ,flatMap(:), ,changes.
@dynamicMemberLookup , , var.

Xcode . : .
Changeable
, , Changeable :
protocol Changeable {
init(copy: ChangeableWrapper<Self>)
}
extension Changeable {
func changing(_ change: (inout ChangeableWrapper<Self>) -> Void) -> Self {
var copy = ChangeableWrapper<Self>(self)
change(©)
return Self(copy: copy)
}
}
changing(:) , , .
, , Changeable:
extension User: Changeable {
init(copy: ChangeableWrapper<Self>) {
self.init(
id: copy.id,
name: copy.name,
age: copy.age
)
}
}
, , — :
let steve = User(id: 1, name: "Steve", age: 21)
let steveJobs = steve.changing { newUser in
newUser.name = "Steve Jobs"
newUser.age = 30
}
, , …

changing(:) , , , :
struct Company {
let name: String
let country: String
}
struct User {
let id: Int
let company: Company
}
let user = User(
id: 1,
company: Company(
name: "NeXT",
country: "USA"
)
)
user, company.name, :
let appleUser = user.changing { newUser in
newUser.company = newUser.company.changing { newCompany in
newCompany.name = "Apple"
}
}
, .
. — ChangeableWrapper:
subscript<T: Changeable>(
dynamicMember keyPath: KeyPath<Wrapped, T>
) -> ChangeableWrapper<T> {
get {
ChangeableWrapper<T>(self[dynamicMember: keyPath])
}
set {
self[dynamicMember: keyPath] = T(copy: newValue)
}
}
, Changeable. Swift - . , .
, :
let appleUser = user.changing { newUser in
newUser.company.name = "Apple"
}
, .

, , , , . Swift , .
. . !