I want to be able to copy a custom class in Swift. So far, so good. In Objective-C I just had to implement the NSCopying protocol, which means implementing
I was looking for a simpler solution. The concept was simple enough, duplicate the data by initialization however in my case the object chain was large and nested.
Luckily my objects were already conforming to Codable so I simply used Encode and Decode to deep-copy the entire object.
class MyObject: Codable, CustomStringConvertible {
var name: String
var description: String { name }
init(name: String) {
self.name = name
}
}
let originalArr = [MyObject(name: "a"), MyObject(name: "b")]
do {
let data = try JSONEncoder().encode(originalArr)
let copyArr = try JSONDecoder().decode([MyObject].self, from: data)
//modify
copyArr.forEach { obj in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
} catch {
fatalError(error.localizedDescription)
}
To simplify future cases we can create a protocol and conform to it, allowing interesting ways to use it.
protocol Copyable: Codable {
func copy() -> Self
}
extension Copyable {
func copy() -> Self {
do {
let encoded = try JSONEncoder().encode(self)
let decoded = try JSONDecoder().decode(Self.self, from: encoded)
return decoded
} catch {
fatalError(error.localizedDescription)
}
}
}
That's it. We can then conform our object to Copyable
class MyObject: Copyable, CustomStringConvertible {
var name: String
var description: String { name }
init(name: String) {
self.name = name
}
}
let a = MyObject(name: "A")
let b = a.copy()
b.name = "B"
print(a.name, b.name) //-> "A B"
We can quickly add support on Array as well:
extension Array where Element: Copyable {
func copy() -> [Element] {
return self.map { $0.copy() }
}
}
let originalArr = [MyObject(name: "a"), MyObject(name: "b")]
let copyArr = originalArr.copy()
copyArr.forEach { (obj) in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]