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(_:).
Related
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.
I'm pretty new to Swift, and although I've read Apple's documentation and many topics and threads about this, I still can't understand what's the difference between { get } and { get set }. I mean, I'm looking for an explanation with a concrete example.
Like, for example:
protocol PersonProtocol {
var firstName: String { get }
var lastName: String { get set }
}
What would be the actual difference between these two properties? I tried to play with these properties in a playground:
struct Person: PersonProtocol {
var firstName: String
var lastName: String
}
var p = Person(firstName: "John", lastName: "Lennon")
print(p.firstName) // John
print(p.lastName) // Lennon
p.firstName = "Paul"
p.lastName = "McCartney"
print(p.firstName) // Paul
print(p.lastName) // McCartney
Did not help... Thanks for your help.
You are creating a variable of type Person and there are no restrictions on that struct. If you instead create a variable of type PersonProtocol then firstName will be read only
var p1: PersonProtocol = Person(firstName: "John", lastName: "Lennon")
print(p1.firstName) // John
print(p1.lastName) // Lennon
p1.firstName = "Paul" <== error: cannot assign to property: 'firstName' is a get-only property
protocol — is a requirement of some minimal interface of the type implementing it.
var name: Type { get } requires type to have property with at least a getter (accessible from outside of the type, not private), i.e. outside code should be able to read value of the property. In the implementing type it could be let name: Type, var name: Type, private(set) var name: Type, fileprivate(set) var name: Type, etc.
var name: Type { get set } requires type to have property with both accessible getter and setter, i.e. outside code should be able to read and write to the property. Here only var name: Type would be allowed.
If protocol requires for getter but you also provide a setter — it's not against protocol requirements.
But if protocol requires for both getter and setter — you must provide both, and not having any of them won't be valid implementation.
Your Person class defined both properties as var(with accessible getter and setter) therefore you can change them both. But PersonProtocol haven't required ability to set firstName.
And as #JoakimDanielson shows, if you will use just interface required by protocol you won't be to change the firstName value.
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.
I've created the following class
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
Then I instantiated a constant value from the class
let john = Person(firstName: "Johnny", lastName: "Applessed")
Question: Why I can change the content of the variable john? Isn't it a constant? Can someone explain that for me, thanks a lot.
john.firstName = "John"
print(john.firstName) // -> John
As #Wain has said – it's due to the nature of reference types. The instance being a let constant only means you cannot assign a new reference to it – but says nothing about the actual mutability of the instance itself.
If you change your class to a struct, you'll see how the behaviour differs with value types, as changing a property changes the actual value of your Person – therefore you are unable to do so if it's a let constant. However I somewhat doubt you'll want to make your Person a struct, as two people with the same name shouldn't be considered to be the same person.
If you only wish your properties to be assigned upon initialisation (and then read-only for the lifetime of the instance), then I would recommend making them let constants (instead of making their setters private). This will ensure that you cannot even change their value from within your class, once assigned.
The rule is as long you give a property a value before the super.init() call – you can make it a let constant (in this case, you just have to assign them in the initialiser before using self).
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
...
The class instance itself is a constant, so you can't change it to reference another instance, but the instance is mutable because it's properties are created as vars.
Change firstName to have a private setter and see what you can do:
private(set) var firstName: String
When you're using a constant instance of a class in swift, doesn't mean you can't change the class attributes. It' means you can't instantiate a new object in this constant
let person = Person(firstName: "Johnny", lastName: "Appleseed")
person = Person(firstName: "John", lastName: "Appleseed") //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
But you can create a constant inside class and set this values in the init
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
//Tip: Don't init the class constants in declaration time or will get the same above error. Just init this constants at constructor/initialization of class.
And Now you have the expected result you want, even if create a 'var' instance of this object
var person = Person(firstName: "Johnny", lastName: "Appleseed")
person.firstName = "John" //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
person = Person(firstName: "John", lastName: "Snow")
person.firstName = "Johnny" //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
Your thinking was not wrong, but a little confuse cause you would be totally right if it was a struct instead a class.
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)}