How do you copy objects in Swift?

10,258

Solution 1

You can implement Client as a struct instead of a class since struct is always passed by value.

struct Client {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

var wrongClient = Client(name: "John", age: 18)
var rightClient = wrongClient
rightClient.age = 99

Assigning wrongClient to rightClient creates a copy. When you update the age of rightClient, wrongClient still remains 18.

Solution 2

There isn't any defined short-hand for it. I'd recommend something like:

class Client {
    let name: String
    let age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    private init(from: Client, age:Int) {
        self.name = from.name
        self.age = age
    }

    func withAge(age:Int) -> Client {
        return Client(from: self, age: age)
    }
}

let right_client = Client(name: "John", age: 9)
let wrong_client = right_client.withAge(42)

Solution 3

There has to be a better way, but my current solution is this:

   init(name: String, age: Int) {
       self.name = name
       self.age = age
   }

   init(from: Client, name: Int? = nil, age: Int? = nil) {
       self.name = name ?? Client.name
       self.age = age ?? Client.age
   }

Solution 4

You could add another init method.

   required init(name: String, age: Int) {
       self.name = name
       self.age = age
   }

   convenience init(from: Client, withAge: Int) {
       self.init(name: from.name, age: withAge)
   }

Solution 5

In our Swift code generator we automatically include a copy constructor for every class. The generator is here: https://github.com/eclipse/agileuml

An example of the constructor, working with inheritance, is:

class Person
{ init() { }

  init(copyFrom: Person) {
    self.name = copyFrom.name
    self.age = copyFrom.age
    self.parents = copyFrom.parents
  }

  func copy() -> Person
  { let res : Person = Person(copyFrom: self)
    return res
  }

  var name : String = ""
  var age : Int64 = 0
  var parents : [Person] = []
}

class Staff : Person
{ override init()
  { super.init() }

  init(copyFrom: Staff) {
    super.init(copyFrom: copyFrom)
    self.staffNo = copyFrom.staffNo
    self.boss = copyFrom.boss
  }

  override func copy() -> Staff
  { let res : Staff = Staff(copyFrom: self)
    return res
  }

  var staffNo : Int = 0
  var boss : [Staff] = []
}

Sequences, sets and maps are also copied element-by-element.

Share:
10,258

Related videos on Youtube

Antoine
Author by

Antoine

Updated on September 14, 2022

Comments

  • Antoine
    Antoine over 1 year

    I have the following class:

    class Client {
      let name: String
      let age: Int
    
      init(name: String, age: Int) {
        self.name = name
        self.age = age
      }
    }
    
    let wrongClient = Client(name: "John", age: 9)
    

    How can I create a new version of wrongClient with the right age?

    I want something like the following:

    let rightClient = Client(wrongClient, age: 42)
    

    For example OCaml lets developers do the following:

    type client = {
      name : string;
      age : int;
    }
    
    let wrong_client = {name = "John"; age = 25}
    let right_client = {wrong_client with age = 42}
    

    Or in Scala:

    case class Client(name: String, age: Int)
    
    val wrongClient = Client(name: "John", age: 9)
    val rightClient = wrongClient.copy(age=42)
    

    EDIT

    I want to experiment with data immutability and data sharing with Swift.

    Because immutable data imply "generating" values from other values, "copying" objects can happen often. So my question really is : how can I easily construct new objects from other objects with Swift?

    EDIT 2

    I'm currently looking at Swiftz' lenses.

  • Antoine
    Antoine about 9 years
    Is there a way to automate this behaviour for every classes?
  • fred02138
    fred02138 about 9 years
    Not that I'm aware of.
  • Ali Aqdas
    Ali Aqdas about 3 years
    Thanks for your answer it's working for me <3