Why am I Allowed to Set a Read Only Property of a Protocol Using a Struct That Inherits Said Protocol

Why am I allowed to set a read only property of a protocol using a struct that inherits said protocol?

What am I doing wrong to make it a true read only property to where I can't set it?

There is a difference between a protocol (a set of rules) and the type (i.e. your struct) that adopts the protocol.

  • Your protocol rule says that readOnlyProperty should be readable.

  • Your struct obeys by making it readable, and also makes it writable. That is not illegal, so all is well — and readOnlyProperty in your struct is read-write.

What would have been illegal would be the inverse, i.e. for the protocol to declare a property read-write but the adopter to declare it read-only. That situation didn't arise in your example, but if it had, the compiler would have stopped you.

Swift protocols mutability

The protocol requirement is

a variable name which can be read

which doesn't mean that the variable in a struct adopting this protocol is necessarily read-only.

In the code you are changing the variable directly in the Driver type, the protocol is not involved.

On the other hand if you annotate the protocol type you get the expected error

var driver : Person = Driver(name: "Ryan")
driver.name = "Changed!" // Cannot assign to property: 'name' is a get-only property

Why I can't use let in protocol in Swift?

"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.

Is there a way to declare protocol property as private?

Conforming to

protocol P {
var value: String { get }

init(value: String)
}

requires a gettable property value with default access. If write access to the
property in the conforming class should be restricted to the class itself
then you can declare it as in Swift readonly external, readwrite internal property:

class C: P {
private(set) var value: String

required init(value: String) {
self.value = value
}
}

let myObject = C(value: "Hello World")
print(myObject.value) // OK
myObject.value = "New Value" // Error: Cannot assign to property: 'value' setter is inaccessible

And if the property should only be set in initializers then make it
a constant:

class C: P {
let value: String

required init(value: String) {
self.value = value
}
}

let myObject = C(value: "Hello World")
print(myObject.value) // OK
myObject.value = "New Value" // Error: Cannot assign to property: 'value' is a 'let' constant

Swift Protocol get only settable?

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.

Swift Protocol - Property type subclass

The problem is with the setter in the protocol.

Let's say you want to GET the panelView from LeftPanelController. That's fine, because LeftPanelView can do everything PanelView can do (and more).

If you want to SET the panelView of LeftPanelController though, you can give it any PanelView. Because you're defining the panelView variable as a LeftPanelView, the setter could sometimes fail.

To fix this, you could do the following in LeftPanelController:

var panelView: PanelView = LeftPanelView()

The implication of this is that you won't be able to access any methods or properties that are specific to LeftPanelView without casting it first. If that's not an issue, then this should fix your problem!

What's the correct way to implement default properties when doing default implementation of protocols in Swift?

It's been a few weeks since I posted this, but I believe what Aaron Brager said is true.

While Protocol-Oriented-Programming is rather new in itself, the idea of protocols has been present in Objective-C for a long time, they are in Swift, and they have their variants in languages such as Java. Due to the nature of protocols and extensions, it looks like doing default implementations of properties is not possible, as extensions won't allow you to set non-computed properties in them.

Swift - Protocol extensions - Property default values

It seems you want to add a stored property to a type via protocol extension. However this is not possible because with extensions you cannot add a stored property.

I can show you a couple of alternatives.

Subclassing (Object Oriented Programming)

The easiest way (as probably you already imagine) is using classes instead of structs.

class IdentifiableBase {
var id = 0
var name = "default"
}

class A: IdentifiableBase { }

let a = A()
a.name = "test"
print(a.name) // test

Cons: In this case your A class needs to inherit from IdentifiableBase and since in Swift theres is not multiple inheritance this will be the only class A will be able to inherit from.

Components (Protocol Oriented Programming)

This technique is pretty popular in game development

struct IdentifiableComponent {
var id = 0
var name = "default"
}

protocol HasIdentifiableComponent {
var identifiableComponent: IdentifiableComponent { get set }
}

protocol Identifiable: HasIdentifiableComponent { }

extension Identifiable {
var id: Int {
get { return identifiableComponent.id }
set { identifiableComponent.id = newValue }
}
var name: String {
get { return identifiableComponent.name }
set { identifiableComponent.name = newValue }
}
}

Now you can make your type conform to Identifiable simply writing

struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}

Test

var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test


Related Topics



Leave a reply



Submit