Dynamically create objects from function - swift

Im sorry in advance if my question is too silly, Im new to OOP and programming generally, but i need to figure out a way to create objects dynamically. For example I have a class with init like this
class User {
var name: String = ""
var age: Int = 0
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
I can create new object by using
var newUser = User(name: "Joe", age: 21)
but i do it manually by hands in code.
How can I automate the process so every time I need to create an object, I pass name, age and object creates automatically with assigning to new variable without mentioning the variable name (for example, there is pattern to for creation a variable, so it does user1, user2, user3 and so on) ? Do I Have to create a specific builder function that creates instances of user?

If you want to create a large list of users for JSON or something without having to assign a bunch of variable names by hand, I would use a Dictionary and dynamically create the key and value. So a function would look like this
var dynamicallyAssignedName: String?
var dynamicallyAssignedAge: Int?
var users: Dictionary<String, User>?
func newUser(name: String, age: Int) {
var createUser = User(name: dynamicallyAssignedName, age: dynamicallyAssignedAge)
users[dynamicallyAssignedName] = createUser
}
Then you can upload the dictionary fairly easily.

You might mean something like this:
let arr = [("Matt", 61), ("Alexey", 123)]
let arr2 = arr.map {User(name:$0.0, age:$0.1)}

Related

SwiftUI : #State var - Cannot use instance member 'cercax' within property initializer; property initializers run before 'self' is available [duplicate]

Seems like I'm having a problem with something that shouldn't be the case... But I would like to ask for some help.
There are some explanations here on the Stack I don't get.
Having two simple classes where one refers to another, as per below:
class User {
lazy var name: String = ""
lazy var age: Int = 0
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
class MyOwn {
let myUser: User = User(name: "John", age: 100)
var life = myUser.age
//Cannot use instance member 'myUser' within property initializer
//property initializers run before 'self' is available
}
I get the commented compile error. May someone please tell me what should I do to solve the case?
As correctly pointed out by vadian you should create an init in such scenarios:
class MyOwn {
let myUser: User
var life: Int
init() {
self.myUser = User(name: "John", age: 100)
self.life = myUser.age
}
}
You can't provide a default value for a stored property that depends on another instance property.
You should declare life like this:
lazy var life:Int = {
return self.myUser.age
}()
Because you are trying to initialise one property(variable) with another during initialisation process. At this time variables are not available yet.

Swift: how closure captures variables of value type?

Take a look at the following code snippet
struct Person{
var name: String
let surname: String
var closure: (()->())?
init(name: String, surname: String){
self.name = name
self.surname = surname
}
}
var person = Person(name: "John", surname: "Lennon")
let cl = {
print(person.name)
}
person.name = "Bill"
cl()
print(person.name)
the output of the above snippet is
Bill
Bill
Can somebody explain how this happens? I thought that since closure is reference type and Person is a value type then when the closure is created it gets its own copy of the Person(since value types are copied on pass), so modifying outer Person should not affect Person that is captured by closure, but it seems that it doesn't work in this way. I'm new to swift and value types, so please don't judge my question too hard.Thank you P.S. I know that we can capture value variable explicitly using capture list and in this case modifying outer variable doesn't affect captured variable. The question is no about this. The question is about the fact that I thought that it should be have the same way even without explicit capture
The behaviour you expect only works when you explicitly pass in a variable to a closure like this:
var person = Person(name: "John", surname: "Lennon")
let cl: (Person) -> () = { person in
print(person.name)
}
cl(person)
person.name = "Bill"
cl(person)
When you implicitly capture a variable in a closure, that variable is always passed by reference. If you want to capture variables by value, you need to explicitly pass them in.

Property initializers run before 'self' is available

Seems like I'm having a problem with something that shouldn't be the case... But I would like to ask for some help.
There are some explanations here on the Stack I don't get.
Having two simple classes where one refers to another, as per below:
class User {
lazy var name: String = ""
lazy var age: Int = 0
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
class MyOwn {
let myUser: User = User(name: "John", age: 100)
var life = myUser.age
//Cannot use instance member 'myUser' within property initializer
//property initializers run before 'self' is available
}
I get the commented compile error. May someone please tell me what should I do to solve the case?
As correctly pointed out by vadian you should create an init in such scenarios:
class MyOwn {
let myUser: User
var life: Int
init() {
self.myUser = User(name: "John", age: 100)
self.life = myUser.age
}
}
You can't provide a default value for a stored property that depends on another instance property.
You should declare life like this:
lazy var life:Int = {
return self.myUser.age
}()
Because you are trying to initialise one property(variable) with another during initialisation process. At this time variables are not available yet.

How can I create a optional value from a my class

I create a class User, and I want to create an optional out of it, compiler then fires an error
class User {
var firstName: String = ""
var lastName: String = ""
}
var Tom = User?(firstName: "Tom", lastName: "Soya")
error: cannot invoke initializer for type 'User?' with an argument list of type '(firstName: String, lastName: String)'
Question: What's wrong with it? Do I need to put more stuff inside class before I can create an optional value? If so what is it?
Thanks
The problem is that your User class has no initializers. Thus, there is only one way to initialize it: namely, by saying User(). So if you are going to define User like this:
class User {
var firstName: String = ""
var lastName: String = ""
}
Then the best you can do is this:
var tom : User? = User()
tom?.firstName = "Tom"
tom?.lastName = "Soya"
#matt's answer is correct, but here's another way, which I think is clearer:
var tom = Optional(User())
See Optional.init(_:).

How to copy a "Dictionary" in Swift?

How to copy a "Dictionary" in Swift?
That is, get another object with same keys/values but different memory address.
Furthermore, how to copy an object in Swift?
Thanks,
A 'Dictionary' is actually a Struct in swift, which is a value type. So copying it is as easy as:
let myDictionary = ...
let copyOfMyDictionary = myDictionary
To copy an object (which is a reference type) has a couple of different answers. If the object adopts the NSCopying protocol, then you can just do:
let myObject = ...
let copyOfMyObject = myObject.copy()
If your object doesn't conform to NSCopying then you may not be able to copy the object. Depending on the object's class it may provide it's own method to get a duplicate copy, or if the object has no internal private state then you could create a new object with the same properties.
[Edited to correct a mistake in the previous answer - NSObject (both the Class and the Protocol) does not provide a copy or copyWithZone method and therefore is insufficient for being able to copy an object]
All of the answers given here are great, but they miss a key point regarding warning you about the caveats of copying.
In Swift, you have either value types (struct, enum, tuple, array, dict etc) or reference types (classes).
If you need to copy a class object, then, you have to implement the methods copyWithZone in your class and then call copy on the object.
But if you need to copy a value type object, for eg. an Array, you can copy it directly by just assigning it to a new variable like so:
let myArray = ...
let copyOfMyArray = myArray
But this is only shallow copying.
If your array contains class objects and you want to make their copy as well, then you have to copy each array element individually. This will allow you to make a deep copy.
This is extra information that I thought would add to the info already presented in the well-written answers above.
Object
class Person: NSObject, NSCopying {
var firstName: String
var lastName: String
var age: Int
init(firstName: String, lastName: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
func copyWithZone(zone: NSZone) -> AnyObject {
let copy = Person(firstName: firstName, lastName: lastName, age: age)
return copy
}
}
Usage
let paul = Person(firstName: "Paul", lastName: "Hudson", age: 35)
let sophie = paul.copy() as! Person
sophie.firstName = "Sophie"
sophie.age = 5
print("\(paul.firstName) \(paul.lastName) is \(paul.age)")
print("\(sophie.firstName) \(sophie.lastName) is \(sophie.age)")
Source: https://www.hackingwithswift.com/example-code/system/how-to-copy-objects-in-swift-using-copy