Swift Protocols: Difference between { get } and { get set } with concrete examples? - swift

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.

Related

Stored property without initial value prevents synthesized initializers

I declare a protocol:
protocol FullNameable {
var fullName: String {get set}
}
then adopt the above protocol for class and struct like below:
struct LectureStruct: FullNameable {
var fullName: String
}
class LectureClass: FullNameable {
var fullName: String
}
But for class I am getting error - Stored property 'fullName' without initial value prevents synthesized initializers. Why this error is not for a struct?
According to swift documentation:
All structures have an automatically generated memberwise initializer,
which you can use to initialize the member properties of new structure
instances. Initial values for the properties of the new instance can
be passed to the memberwise initializer by name
let vga = Resolution(width: 640, height: 480)
Unlike structures, class instances don’t receive a default memberwise
initializer.
In your case, you can instantiate LectureStruct with:
let lecture = LectureStruct(fullName: "Match Lecture")
In order to do the same with LectureClass you'll need to define an init method like so:
class LectureClass: FullNameable {
var fullName: String
init(fullName: String) {
self.fullName = fullName
}
}

In Swift do you not have to use a getter and setter when creating an object type, such as class, that conforms to a protocol requiring so?

protocol UserType {
var name: String { get }
var age: Int { get set }
struct Person: UserType {
var name: String //<- why is this okay?
var age: Int //<- why is this okay? dont we need a getter setter
}
let somePerson = Person(name: "Billy", age: 22)
Why in the above examples, we dont use get constructs or set constructs for age and a get construct for name? Is it because in the protocol when the property is said to "{ get set }" that essentially means it has to be able to be read and has to be able to be changed, which can be done through a declaration in the syntax of a stored property?
Thanks!
protocol UserType {
var name: String { get }
var age: Int { get set }
}
The important thing to understand is that { get } and { get set } are merely notations. They have nothing to do with "getter and setter". They are merely ways of describing to the compiler what the protocol's requirements are. (You could argue that the notation is confusing or misleading, but it's what we've got, so we have to live with it.)
So, basically, all this says is:
An adopter must declare a name String instance property.
An adopter must also declare an age Int instance property and that property must be writable.
That's all it means. Well, you have satisfied those requirements in your adopter (Person). Your code is legal. The end.
One thing to note first: Every property has an implicit getter and setter unless otherwise stated. e.g. a simple var age: Int implicitly has a get and set, and let age: Int implicitly has a get. When you declare a manual getter and setter, you're overriding something, not creating something that didn't otherwise exist.
As for protocols, in explicit terms:
var name: String { get } means the adhering Type must have a property with a signature of name:String that can be read, AKA. get
var age: Int { get set } means the adhering Type must have a property with a signature of age:String that can be read or assigned, AKA get and set
Note that these rules are inclusive not exclusive. The requirements don't care how they are satisfied, and don't disallow anything else.
This means with your given example, a property of let name: String would work because it only requires access, but let age: Int wouldn't work, because it cannot be changed. It also doesn't care about internal details, so using computed properties, private setters, etc. is fine as long as they have the necessary access.

Literal Convertibles in Swift

I want to know how Literal Convertibles work in Swift. The little I know is that the fact that, in var myInteger = 5, myInteger magically becomes an Int is because Int adopts a protocol, ExpressibleByIntegerLiteral and we don't have to do var myInteger = Int(5). Similarly String, Array, Dictionary etc all conform to some Literal protocols.
My Question is
Am I right in my little understanding of Literal Convertibles?
How can we implement these in our own types. For example
class Employee {
var name: String
var salary: Int
// rest of class functionality ...
}
How can I implement Literal Protocols to do var employee :Employee = "John Doe" which will automatically assign "John Doe" to employee's name property.
You are partially correct in your understanding of the various ExpressibleBy...Literal protocols. When the Swift compiler parses your source code into an Abstract Syntax Tree, it already identified what literal represents what data type: 5 is a literal of type Int, ["name": "John"] is a literal of type Dictionary, etc. Apple makes the base type conform to these protocols for the sake of completeness.
You can adopt these protocols to give your class an opportunity to be initialized from a compile-time constant. But the use case is pretty narrow and I don't see how it applies to your particular situation.
For example, if you want to make your class conform to ExpressibleByStringLiteral, add an initializer to set all your properties from a String:
class Employee: ExpressibleByStringLiteral {
typealias StringLiteralType = String
var name: String
var salary: Int
required init(stringLiteral value: StringLiteralType) {
let components = value.components(separatedBy: "|")
self.name = components[0]
self.salary = Int(components[1])!
}
}
Then you can init your class like this:
let employee1: Employee = "John Smith|50000"
But if you dream about about writing something like this, it's not allowed:
let str = "Jane Doe|60000"
let employee2: Employee = str // error
And if you pass in the wrong data type for salary, it will be a run time error instead of a compile-time error:
let employee3: Employee = "Michael Davis|x" // you won't know this until you run the app
TL, DR: it is a very bad idea to abuse these ExpressibleBy...Literal types.
This can be a scenario to work with Convertibles in custom types.
struct Employee : ExpressibleByStringLiteral {
var name: String = ""
init() {}
init(stringLiteral name: String) {
self.name = name
}
}
func reportName(_ employee: Employee) {
print("Name of employee is \(employee.name)")
}
reportName("John Doe") //Name of employee is John Doe

Why Are My Default Property Values Still Showing as Parameters in Init()?

I have a protocol that describes a marine water parameter that needs tested:
protocol Parameter {
var name: String { get }
var unit: Unit { get }
var value: Double { get }
}
I have a struct, Calcium, that conforms to Parameter:
struct Calcium: Parameter {
var name: String = "Calcium"
var unit: Unit = UnitDispersion.partsPerMillion
var value: Double
}
Since the name and unit parameters of Calcium have default values, why do I need to provide them in the init method? Shouldn't I only need to provide a value for value?
I am trying to understand protocol-oriented-programming and would really appreciate a little guidance here.
This has nothing whatever to do with protocols.
You do not have to provide an initializer for anything but the value. But you have not provided any initializer. Therefore the only initializer you have is the one provided automatically, and that initializer is the memberwise initializer which wants parameters for all your properties.
If you don't like that, write an initializer yourself:
struct Calcium: Parameter {
var name: String = "Calcium"
var unit: Unit = UnitDispersion.partsPerMillion
var value: Double
init(value:Double) {self.value = value}
}
Now it is legal to say:
let c = Calcium(value:2)

Where is the property that do not conform to the protocol goes?

Teacher & TeamMate are two protocols. The class Coach conforms to those protocols.
protocol Teacher {
var firstName: String { get }
var lastName: String { get }
var title: String { get }
}
protocol TeamMate {
var firstName: String { get }
func role()
}
class Coach: Teacher, TeamMate {
var firstName: String
var lastName: String
var title: String
func role() {
print("coach the team")
}
init(firstName: String, lastName: String, title: String){
self.firstName = firstName
self.lastName = lastName
self.title = title
}
}
var member: TeamMate = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
I've created a variable named member of type coach meanwhile it conforms to the TeamMate type (Please correct me if the description is not accurate)
I need to initialise all the properties defined in the Coach class when I create the member object. I.e.(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach"). However, in the end, there is only one property firstName and one method role() inside the member instance.
Question: How the properties (lastName: "Yakuza", title: "LA Coach") were processed? Is that they were created firstly and then cut away or just hidden?
Thanks a lot for your kind help and time.
I've created a variable named member of type coach meanwhile it conforms to the TeamMate type
var member: TeamMate = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
I suspect this line of code isn't doing what you think it's doing. Your member variable isn't of type Coach, you've upcast to the abstract type TeamMate (a protocol which Coach conforms to). Therefore when using your variable, you can only access members that TeamMate defines (firstName and role()) – which is the exact behaviour you're seeing.
Unless you need to you shouldn't upcast your variables, as upcasting loses type information. In most cases, you should just let Swift infer the correct type for you. In your case member should almost certainly be of type Coach (you can always freely pass it to anything expecting a TeamMate, as Coach conforms to this protocol).
Therefore, you just want to drop the explicit type annotation:
var member = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
You should also make it a let constant if you don't plan on mutating it.