Read-Only Properties of Protocols in Swift

Read-only properties of protocols in Swift

There's no way to specify in a protocol that you must have a read-only property. Your protocol asks for a simpleDescription property, and allows but does not require a setter.

Note also that the only reason you may mutate simpleDescription is because you know your a is of type SimpleClass. If we have a variable of type ExampleProtocol instead...

var a: ExampleProtocol = SimpleClass()
a.simpleDescription = "newValue" //Not allowed!

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.

Define a read-only property in Swift

You can use a Computed Property which (like a method) can be overridden.

class Parent: UIView {
var itemCount: Int { return 0 }
}

class Child: Parent {
override var itemCount: Int { return 1 }
}

Update (as reply to the comment below)

This is how you declared and override a function

class Parent: UIView {
func doSomething() { print("Hello") }
}

class Child: Parent {
override func doSomething() { print("Hello world!") }
}

How to set a readwrite internal, readonly external property in protocol

You must remove any private function from protocol (Since it's meaningless). Nothing is private inside a protocol( setter or function or etc. )

protocol SampleProtocol {
associatedtype Item: Switchable

var savedPath: String { get }
var items: [Item] { get }
var currentItem: Item? { get }
}

And you should then implement the class access controls like this:

class SampleClass {
private(set) var items: [SampleModel] {
set {
archive(with: newValue)
} get {
return unarchive()
}
}

private func archive(with addresses: [SampleModel]) { /* ... */ }
private func unarchive() -> [SampleModel] { /* ... */ }
}

Why declare readonly property in protocol?

So that it's not settable from outside the class/struct. Imagine your API returned some instance of a protocol that has a get and set property (in your protocol), then anyone getting this instance would be able to set the value!

Also get and set properties can't be constants:

protocol RWProt {
var value : Int { get set }
}

// Error: Type 'Value' does not conform to protocol 'RWProt'
struct Value : RWProt {
let value = 0
}

This however works:

protocol Read {
var value : Int { get }
}

struct Value : Read {
var value = 0

mutating func change() {
value++
}
}

The protocol only needs the value to be gettable, so get protocols properties are not get only but rather get or set

Okay, here is another example:

import Foundation

public protocol ExternalInterface {
var value : Int { get }
}


private struct PrivateStuff : ExternalInterface {
var value = 0

mutating func doSomePrivateChangingStuff() {
value = Int(arc4random())
}
}



public func getInterfaceToPrivateStuff() -> ExternalInterface {
var stuff = PrivateStuff()
stuff.doSomePrivateChangingStuff()
return stuff
}




// In another file:

let interfaceToSomethingICantChange = getInterfaceToPrivateStuff()

// error: cannot assign to property: 'value' is a get-only property
interfaceToSomethingICantChange.value = 0

Swift protocol settable property through a read-only property

Change your protocol declaration to this:

protocol AProtocol:class {
var name: String { get set }
}

Otherwise, it is taken by default as a value type. Changing a value type's property replaces the value type instance (as shown by the setter observer). And you can't do that if the reference is a let reference.

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

Set value of read-only stored property during initializing in Swift

You should be able to just add a setter to it and store the information in an inner coordinate value. Since you have a getter it is still conforming to the protocol:

var innerCoordinate: CLLocationCoordinate2D

var coordinate: CLLocationCoordinate2D {
get {
return self.innerCoordinate
}
set {
self.innerCoordinate = newValue
}
};

init(coordinate:CLLocationCoordinate2D) {
super.init()
self.innerCoordinate = coordinate
}

This is actually how I implement readonly and private properties (with protocols and the factory pattern). I setup protocols with the public interface and classes with private variables and setters. It is actually super clean way to setup your code (and gets around the lack of protected/private properties in Swift).


Here is a abstracted example of what I am talking about (if you care):

// this is your MKAnnotation in this example
protocol SomeProtocol {
var getterProperty: String { get }
var setterProperty: String { set get }

func publicFunction(someStirng: String) -> ();

}

// setup a function that returns a class conforming to your needed protocol
func SomeClassMaker() -> SomeProtocol {
// your internal class that no one can access unless by calling the maker function
class SomeClassInternal: NSObject, SomeProtocol {

// private and no one can get to me!
var innerSetterProperty = "default setter";

var getterProperty = "default getter"

var setterProperty: String {
get {
return self.innerSetterProperty;
}
set {
"hit"
self.innerSetterProperty = newValue
}
}

func publicFunction(someString: String) -> () {
// anyone get me
self.getterProperty = someString;
}

func privateFunction() -> () {
// no one can get me except internal functions
}

}

return SomeClassInternal();
}


// create the class
var classInstance = SomeClassMaker();

// totally fine!
classInstance.setterProperty = "secret string"
// prints "secret string"
classInstance.setterProperty;

// error! no public setter for "getter"
classInstance.getterProperty = "another secret"

classInstance.publicFunction("try secret again")
// prints "try secret again"
let blahed = classInstance.getterProperty

// error!
classInstance.privateFunction()

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: Cannot override mutable property with read-only property 'xxx'

The protocol requires that the adopting class implements a property testTitle with a getter but it does not forbid to declare the property with getter and setter.

To override a property you have to override the (entire) signature in the class, not the protocol requirement.

As testTitle is declared as read/write you cannot override it as read-only.



Related Topics



Leave a reply



Submit