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
Swift Generic Protocol Function Parameters
Get Element from Array of Dictionaries According to Key
New Value Is Only Available in Sendasynchronousrequest - Swift
Any Way to Get a Gif as a Background with Swiftui
Swift:Program for Addition of 2 Numbers Using Closure
Swift Conforming Multiple Protocols Inherits from Same Protocol with Associated Type
Value' Is Inaccessible Due to 'Internal' Protection Level
Swiftui: Stop an Animation That Repeats Forever
Xctest Unit Test Data Response Not Set in Test After Viewdidload
Can Openssl Be Bundled for Wget Wrapper App to Reference in Xcode Project
How to Give Dynamic Height to Uitableview
Error: Argument Type Double/String etc. Does Not Conform to Expected Type "Anyobject"
Initializer for Conditional Binding Must Have Optional Type, Not '[String:Any]'
How to Set Up Multiple Combine Timer Publishers
No Such Module Crashlytics - Pod Seems to Be Missing
How to Figure Out the Day Difference in the Following Example