83
votes

I have a doubt about protocols in Swift about the use of var and the keywords { get set }.

From Apple documentation:

If a protocol requires a property to be gettable and settable, that property requirement cannot be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it is valid for the property to be also settable if this is useful for your own code.

Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }.

I can't understand why I can't use let. A var in a protocol with only get isn't just a let?

Something like this:

protocol someProtocol 
{
   var someProperty: String { get }
}

it would not be just:

protocol someProtocol 
{
   let someProperty: String
}

I'm missing something?

4
a gettable property can still change values - the protocol implementor can always return different values, the protocol caller simply can't set them. let, on the other hand, defines a constant. it can only be set once and can not have a different value.pvg
@pvg, why didn't you post this as an answer?0x416e746f6e
yup, this is the correct answer here.sunshinejr
@AntonBronnikov Answers to simple questions often end up a competition of who can type the wordiest, least comprehensible thing. For little things, easier to just comment. Also, lazy.pvg
@pvg, I get your point. One should please to be right.0x416e746f6e

4 Answers

69
votes

"A var in a protocol with only get isn't just a let?" No. A let indicates a constant. But that is not the case here. Consider the following:

protocol SomeProtocol {
    var someProperty: String { get }
}

class SomeClass : SomeProtocol {

    var someProperty: String = ""

    func cla () {
        someProperty = "asd"
    }
}

let someInstance = SomeClass()

print(someInstance.someProperty) // outputs ""
someInstance.cla()
print(someInstance.someProperty) // outputs "asd"

The protocol specifies what the conforming class shows to the outside - some property of type String named someProperty which you can at least get.

If the protocol specifies { get } your class can choose to conform via let someProperty: String = "" but it can similarly choose to conform via the above code. If on the other hand the protocol specifies { get set } you cannot use let in the implementation but have to make it set-able as well.

A protocol simply cannot define that a value has to be constant - neither should it, that is an implementation detail that has to be taken care (or decided about) by the class / struct that implements it.

48
votes

The difference is between

protocol MyProtocol {
    let someProperty: String
}

which makes no sense — a protocol isn't supposed to dictate how someProperty is defined/stored, only that it's available as a property. It could be either a computed or stored property, but that's for the implementer to decide, not the protocol itself.

and

protocol MyProtocol {
    var someProperty: String { get }  // abstract interface
}

struct MyStruct: MyProtocol {
    let someProperty: String  // concrete implementation: stored property
}

struct OtherStruct: MyProtocol {
    let i: Int
    var someProperty: String { return "\(i)" }  // concrete implementation: computed property
}

which is perfectly allowed!

3
votes

I think a protocol can require that a structure has something, but it can't restrict functionality of struct or object. That shouldn't prevent you from doing what you'd probably like to do in code, for example using a var in the protocol and a let for the implementation is acceptable.

protocol MyProtocol {
    var trythis: Int { get }
}

struct MyStructure: MyProtocol {
    let trythis: Int
}
0
votes

A property declared with let is considered read-only under the hood. For this reason, a protocol can require that a property be a constant by setting it read-only. This deduction can be verified using some of the Objc runtime functions property_getAttributes.

protocol SomeProtocol {
    var someTypeProperty: Int { get }
}

struct Foo: SomeProtocol {
    let someTypeProperty: Int
}