It often happens that we need to copy an object, changing some of its properties, but keeping the rest unchanged. There is a function for this task copy()
.
This is an excerpt copy()
from the Kotlin documentation for the method . In our native Swift language, this means something like this:
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
}
Looks delicious, doesn't it?
, 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
:
- , , , - .
- "". ,
willSet
didSet
. - , .
, — , :
// , `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 , .
. . !