25

I'm playing around with protocols and how to conform to them.

protocol Human {    
    var height: Int { get set }    
}

struct Boy: Human { 
    var height: Int  { return 5 } // error!
}

I'm trying to learn different ways that I can implement set and get. However the code above throws the following error:

type 'Boy' does not conform to protocol 'Human'

However writing as below won't have any errors:

struct Boy: Human { 
    var height = 5 // no error
}

I don't understand the difference nor what exactly needs to be implemented when you can also set a variable. I looked into different questions and tutorials but they all just write and go without any deeper explanation.

EDIT: make sure you see Imanou's answer here. It greatly explains the different scenarios.

zeytin
  • 5,545
  • 4
  • 14
  • 38
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • 1
    FWIW `struct boy : Human { let height = 5 // error! }` would also produce an error. The reason is mentioned in [this](https://stackoverflow.com/questions/40820913/how-can-you-conform-protocol-variables-set-get/40820968?noredirect=1#comment85239656_40820968) comment by Martin. *`var` declares a variable, and `let` a constant. As stored properties, the first is read/write and the second read-only* – mfaani Mar 05 '18 at 19:02

2 Answers2

38

From the Swift Reference:

Property Requirements

...
The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type.
...
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 }.

In your case

var height: Int  {return 5} // error!

is a computed property which can only be get, it is a shortcut for

var height: Int {
    get {
        return 5
    }
}

But the Human protocol requires a property which is gettable and settable. You can either conform with a stored variable property (as you noticed):

struct Boy: Human { 
    var height = 5
}

or with a computed property which has both getter and setter:

struct Boy: Human { 
    var height: Int {
        get {
            return 5
        }
        set(newValue) {
            // ... do whatever is appropriate ...
        }
    }
}
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • `var height = 5` how does this make conform to both get and set? – mfaani Nov 26 '16 at 17:23
  • 1
    @Honey: `var` declares a *variable* which can be both set and get. – Martin R Nov 26 '16 at 17:24
  • 1
    @Honey: `{ get set }` in the protocol indicates a *gettable and settable property,* and it does not matter if that is a stored or a computed property. I have added a link to the documentation. – Martin R Nov 26 '16 at 17:42
  • Thanks for the edit. Using your answer I was able to write an answer on my own. So you should only be using computed properties if 1) you have a good reason ie you want to compute something based on something else. Otherwise it doesn't make sense right? 2) You ALWAYS need a stored property to back it up right? 3) in the stored property implementation which you did... simply using `var` leaves the door open for the variable to be set in a very normal way. right? – mfaani Nov 26 '16 at 18:10
  • 1
    @Honey: "good reason" or "anything special" is a bit vague. A stored property provides storage for a value, a computed property doesn't. A computed property might also save a value to a database, for example. A read-only computed property might need no backing store at all. – Martin R Nov 26 '16 at 18:48
  • when you compute doesn't provide storage, you mean it will never have a memory location and *always* it needs to read/use the stored property and then compute to give a value? – mfaani Nov 26 '16 at 20:20
  • 1
    @Honey: *If* the setter of a computed property wants to persist the information then it needs *some* backing store. That can be a stored property, the user defaults, a file, a database, a web service, etc. But the *language* does not require that. A "do-nothing" setter as above is valid Swift code (even if useless :) – Martin R Nov 26 '16 at 21:57
  • "var declares a variable which can be both set and get." you mean inherent in the definition of the `var` syntax is that all variables are settable and gettable unless you specify otherwise? – mfaani Mar 05 '18 at 15:45
  • 1
    @Honey: `var` declares a variable, and `let` a constant. As *stored properties,* the first is read/write and the second read-only. – Martin R Mar 05 '18 at 18:33
  • The intriguing part here is "// ... do whatever is appropriate ..." in set. Simply `height = newValue` will give these warnings: `Attempting to modify 'height' within its own setter`, and `All paths through this function will call itself`. Very confused... – koen Apr 08 '19 at 15:31
  • 1
    @Koen: Compare my above comment: *“If the setter of a computed property wants to persist the information then it needs some backing store. ...”* – Or simply use a *computed property:* `var height = 5` – Martin R Apr 08 '19 at 16:29
17

Prerequisite:

Read my blog post on Swift Protocol Compile time check

It will answer to A LOT of your questions. Then read the rest.

Go into your playground and just write the snippet below:

var height: Int {
    get {
        return 5
    }
}    

or similarly:

var height: Int {
    return 5
}    

Try to print height's value, obviously works. So far so good

print(height) // prints 5

However if you try to set it to a new value then you'll get an error:

height = 8 // ERROR  

error: cannot assign to value: 'height' is a get-only property


Answer:

Based on Martin's answer, I first wrote:

set(newValue) {
    height = newValue 
}

Which put a ton of load on my memory and led me to this question. Please take a look. So then I was figuring out what to write, and I kind of understood that if you don't want to do anything special you shouldn't be using computed properties and instead you should just use normal stored properties.

So I wrote a similar code

protocol Human {

    var height: Float {get set}

}

struct Boy: Human {
    
    // inch
    var USheight : Float
    
    // cm
    var height: Float {
        get {
            return 2.54 * USheight
        }
        set(newValue) {
         USheight = newValue/2.54
            
        }
    }
}

// 5 ft person
var person = Boy(USheight: 60)
 // interestingly the initializer is 'only' based on stored properties because they
 // initialize computed properties. 


// equals to 152cm person
print(person.height) // 152.4

Pro tip: When should you should make your properties read-only?

Normally if you make a property to be read-only ie { get } it's because those properties are computed and you don't want the object to have control over it.

Example you have a JSON object. It has multiple big objects like:

JSONData
 - userInfo (name, address, age)
 - devices (iPads, iPhones, Mac books)
 - credentials (basic iCloud, pro iCloud, celebrity)

by making the role a read-only you're only allowing the server to tell the code base the role of the user.

protocol Credentials {
    var role: String { get }
    
    init(person: Person)
}

class Person {
    var userInfo: String
    var devices: [String]
    var creds: Credentials {
        Credentials(person: self)
    }
    
    init(userInfo: userInfo, devices: [String]) {
        self.userInfo = userInfo
        self.devices = devices
    }
}
mfaani
  • 33,269
  • 19
  • 164
  • 293