42

why can I do this without any error:

var testDto = ModelDto(modelId: 1)
testDto.objectId = 2

while I define this:

protocol DataTransferObject {
    var objectType: DtoType { get }
    var parentObjectId: Int { get set }
    var objectId: Int { get }
    var objectName: String { get set }
}

struct ModelDto: DataTransferObject {
    var objectType: DtoType
    var parentObjectId: Int
    var objectId: Int
    var objectName: String

    init(modelId: Int) {
        self.objectType = DtoType.Model
        self.objectId = modelId
        self.parentObjectId = -1
        self.objectName = String()
    }
}

If the definition in my protocol is mostly ignored (getter, setter definition), why should I use them anyway?

Karthik Kumar
  • 1,375
  • 1
  • 12
  • 29
Artur Pajonk
  • 443
  • 1
  • 4
  • 5
  • you've missed something very important here. your protocol is not being considered because you're not referencing it. you are choosing to refer to ModelDto directly and not via DataTransferObject. See my answer below. – Patrick Goley Jul 20 '16 at 13:32

6 Answers6

62

Apple states in the "Swift Programming Languages Documentation":

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.

For this reason, the five following Playground code snippets are all valid:

Example #1: constant property

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

Example #2: variable property

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String        
}    

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Example #3: computed property (get only)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

let scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

Example #4: computed property (get and set)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        get {
            return name
        }
        set {
            name = newValue
        }
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Example #5: private(set) variable property

/* Duck.swift located in Sources folder */

protocol FullyNamed {
    var fullName: String { get }
}

public struct Duck: FullyNamed {
    public private(set) var fullName: String
    
    public init(fullName: String) {
        self.fullName = fullName
    }

    public mutating func renameWith(fullName: String) {
        self.fullName = fullName
    }
}

/* Playground file */

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.renameWith("Scrooge H. McDuck")
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Apple also states:

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.

For this reason, the two following Playground code snippets ARE NOT valid:

Example #1: constant property

protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'

Example #2: computed property (get only)

protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'

Example #3: computed property (get only)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String {return "Scrooge McDuck"}
    
    init(fullName: String) {
        self.fullName = fullName 
  // Error Message Cannot assign to Property: "FullName" is get only
    }
}
byaruhaf
  • 4,128
  • 2
  • 32
  • 50
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • This is awesome. Isn't your example 5 is a bit misleading? While the `fullName` isn't settable from a property level...the user of the class can set it directly through initialization. – mfaani Jun 11 '18 at 19:08
  • Nice explanation but it needs to fix examples in terms of private accessibility otherwise compiler will throw an error. – zeytin Jan 01 '21 at 11:19
32

As per the official documentation:

The getter and setter requirements can be satisfied by a conforming type in a variety of ways. If a property declaration includes both the get and set keywords, a conforming type can implement it with a stored variable property or a computed property that is both readable and writeable (that is, one that implements both a getter and a setter). However, that property declaration can’t be implemented as a constant property or a read-only computed property. If a property declaration includes only the get keyword, it can be implemented as any kind of property.

mfaani
  • 33,269
  • 19
  • 164
  • 293
Ankit Goel
  • 6,257
  • 4
  • 36
  • 48
  • 5
    Strange Swift! Get and Set means gettable and settable an Get only means do what you want? :) – Artur Pajonk Jun 01 '15 at 13:27
  • Although I believe this is messy because from my point of view using get or get/set declaration in protocols is nearly useless I will use the private (set) in ModelDto struct declaration like: private (set) var objectId: Int – Artur Pajonk Jun 01 '15 at 14:38
  • It´s also very funny to see, this question got down voted two times ;) although it describes a really strange behavior in swift if you ask me. – Artur Pajonk Jun 01 '15 at 14:43
  • 2
    @ArturPajonk, I can't see anything strange in this behavior. Your struct `ModelDto` does have either getter and setter for `objectId`. It conforms to `DataTransferObject` protocol, by having the getter, but as long as you use `ModelDto` directly, it does have a setter exposed. So you are free to call it. If you wished your setter hidden, you could cast `ModelDto` to `DataTransferObject` – igrek Sep 30 '16 at 14:48
  • 4
    Even I Dont find this strange. Thats what protocols do. They define rules about what must be implemented. {get} says that the conforming thing must have a getter. It may or may not have a setter. – BangOperator Jan 11 '17 at 04:55
  • @ArturPajonk `get` means this value **must** exist. In reality that's all that it means. It doesn't apply any restrictions on the conforming types. However if you have `set`, then restrictions are applied to the conforming types. I highly recommend that you see [Imanou's answer](https://stackoverflow.com/a/38281420/5175709) below. It's awesome – mfaani Jun 11 '18 at 19:03
4

Consider the following:

var testDto = ModelDto(modelId: 1)

The variable testDto type here is known to be ModelDto. ModelDto is known to have a mutable variable var objectId: Int. You're free to modify objectId because you're accesses the object through the ModelDto interface and not via the protocol interface where it is only gettable.

Try the following:

var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error

The above example shouldn't compile. Because the type of testDto is only known to be DataTransferObject, we don't know that the underlying implementation has a settable property. We only know about the gettable property declared in the protocol.

In short, you've declared ModelDto to have a get/set variable, so it would be quite strange indeed if Swift did not let you set it. Having a get only variable would rely on you referncing the object via the protocol or changing objectId on ModelDTO to be a let variable.

EDIT: To address your confusion about why ModelDto is allowed to have a settable variable. It is the same as how ModelDto is allowed to have other functions than the ones defined in the protocol. Getters and setters are actually just functions, so the protocol requiring a getter does not preclude an implementation from also having a setter. The same is possible in Objective C. Protocols are descriptive, not restrictive.

Patrick Goley
  • 5,397
  • 22
  • 42
3

I'm answering the question in it's generic sense.

Before addressing the question you must know what does get & set mean.

(If you'r coming from an Objective-C world:) get means readOnly, that is I'm allowed to know the number of legs an animal has. I'm not allowed to set it. get & set together means readWrite ie I'm allowed to know the weight of an animal while I'm also able to set/change the weight of an animal

With the following example.

protocol Animal {
    var weight : Int { get set }
    var limbs : Int { get }
}

If you only have getter, and attempt to hide setter (by using private (set)... then you WON'T get an error ... it's likely what you wanted and how it must be done!

Likely what you intended:

class Cat : Animal {
    private (set) var limbs: Int = 4 // This is what you intended, because you only have get requirements...and don't want any conforming type to be able to set it ie don't want others do catInstance.limbs = 22
    var weight: Int = 15

}

var smallCat = Cat()
smallCat.weight = 20 // Good!

// attempting to set it will create an error!!!
smallCat.limbs = 5 // Error: Cannot assign to property: 'limbs' setter is inaccessible

Likely what you didn't intend:

class Panda : Animal {
    var limbs: Int = 4 // This is OK, but it kinda defeats the purpose of it being a get only
    var weight: Int = 200   
}

var littlPanda = Panda()

littlPanda.weight = 40 // Good
littlPanda.limbs = 30 // NO Error!!! Likely unintended

Basically with {get} there is still some extra work to be done which the compiler doesn't tell you ... YOU must add private (set) to achieve the intended behavior


If your property has setter and you attempt to hide setter then you will actually see an error.

class Dog : Animal {
    private (set) var limbs: Int = 4
    private (set) var weight: Int = 50  // Error: Setter for property 'weight' must be declared internal because it matches a requirement in internal protocol 'Animal'
}

You're not allowed to hide, because you promised to provide a setter...

mfaani
  • 33,269
  • 19
  • 164
  • 293
2

The behavior you are seeing on your code sample is called member hiding. Member hiding happens in object oriented languages when new a member is declared with the same name or signature of an inherited one, so by having: var objectId: Int in your struct implementation, you are effectively creating a new member called objectId and hiding the property inherited from the protocol.

In order to honor the contract between your struct and your protocol, objectId could be declared as:

  let objectId: Int = 1

or

var objectId: Int {
        get {
            return 1
        }
    }
omaraguirre
  • 154
  • 5
1

In your class, you create a stored property named objectId. In your protocol, you specify that the property needs a getter – that is its only requirement.

If you wanted it to be a computer property, like you expect it to, you need to declare objectId with the following:

var objectId: Int{ return (someNumber) }

Without the closure to compute the value, it is, by default, a stored property.

erdekhayser
  • 6,537
  • 2
  • 37
  • 69