I have a doubt about protocols in Swift about the use of var and the keywords { get set }.
From Apple documentation:
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. 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.
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 }.
I can't understand why I can't use let. A var in a protocol with only get isn't just a let?
Something like this:
protocol someProtocol
{
var someProperty: String { get }
}
it would not be just:
protocol someProtocol
{
let someProperty: String
}
I'm missing something?
"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.
The difference is between
protocol MyProtocol {
let someProperty: String
}
which makes no sense — a protocol isn't supposed to dictate how someProperty is defined/stored, only that it's available as a property. It could be either a computed or stored property, but that's for the implementer to decide, not the protocol itself.
and
protocol MyProtocol {
var someProperty: String { get } // abstract interface
}
struct MyStruct: MyProtocol {
let someProperty: String // concrete implementation: stored property
}
struct OtherStruct: MyProtocol {
let i: Int
var someProperty: String { return "\(i)" } // concrete implementation: computed property
}
which is perfectly allowed!
I think a protocol can require that a structure has something, but it can't restrict functionality of struct or object. That shouldn't prevent you from doing what you'd probably like to do in code, for example using a var in the protocol and a let for the implementation is acceptable.
protocol MyProtocol {
var trythis: Int { get }
}
struct MyStructure: MyProtocol {
let trythis: Int
}
A property declared with let is considered read-only under the hood. For this reason, a protocol can require that a property be a constant by setting it read-only. This deduction can be verified using some of the Objc runtime functions property_getAttributes.
protocol SomeProtocol {
var someTypeProperty: Int { get }
}
struct Foo: SomeProtocol {
let someTypeProperty: Int
}
Related
I have the following piece of code. The protocols and extensions I've added don't seem to be doing quite what I expect. What am I missing?
struct foo {
var getFoo: String
var getBar: bar
struct bar {
var getBar: String
}
}
protocol FooProtocol {
var getFoo: String { get }
var getBar: BarProtocol { get }
}
extension foo: FooProtocol {} // Type 'foo' does not conform to protocol 'FooProtocol'
protocol BarProtocol {
var getBar: String { get }
}
extension foo.bar: BarProtocol {}
The problem in this code is the line extension foo: FooProtocol {}, where we're trying to say that, in effect foo.getBar.getBar should be a valid property. However, Xcode throws up a compilation error saying "Type 'foo' does not conform to protocol 'FooProtocol'.
I can fix this by changing the following:
protocol FooProtocol {
var getFoo: String { get }
var getBar: foo.bar { get } // instead of var getBar: BarProtocol { get }
}
However, it seems the line extension foo.bar: BarProtocol {} should mean FooProtocol doesn't care if we give it foo.bar or BarProtocol. What am I missing here?
#Drobs answer was correct. Here's a bonus question.
BONUS
Now suppose I need to implement another struct that conforms to FooProtocol. Neither of these approaches work. What's the fix?
struct FooImplementation: FooProtocol { // Type 'FooImplementation' does not conform to protocol 'FooProtocol'
var getFoo: String
var getBar: BarProtocol
}
Do I need to use some sort of typealias to achieve this?
You want to use an associatedtype in the protocol. In that case it essentially says give me a type that implements the protocol BarProtocol.
protocol FooProtocol {
associatedtype BarType: BarProtocol
var getFoo: String { get }
var getBar: BarType { get }
}
protocol BarProtocol {
var getBar: String { get }
}
struct Foo: FooProtocol {
var getFoo: String
var getBar: Bar
struct Bar: BarProtocol {
var getBar: String
}
}
I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"
I need to pass models of type decodable and access title and subtitle from it so because Decodable doesn't have title and subtitle properties I implemented an extension for decodable and added a title and subtitle properties to Decodable So any object of type Decodable could write decodableObject.title so when I pass object which conform to decodable and contains title and subtitle properties I need then to access the data of it not Docodable object data but what happens that it just access Decodable extension properties so to access my target object I should make downcasting to this class which then I should write implementation for each model type
//decalring struct which conforms to Decodable
struct TestModel:Decodable {
var title:String?
}
//added property
extension Decodable {
var title:String?{
return "Decodable base"
}
}
func setup(){
var testModel = TestModel()
testModel.title = "Subclass"
checkProperties(model: testModel, mod: TestModel.self)
}
func checkProperties<T:Decodable>(model:Any,mod:T.Type){
typealias MMM = T
let title = (model as! MMM).title
print("type is \(title)")
}
// the resutl will be "Type is Decodable Base"
I need the expected result to be
// the result will be "Type is subclass"
Well, maybe this helps you:
protocol HasTitle {
var title: String? { get }
}
extension Decodable {
var title:String? {
return "Decodable base"
}
}
struct TestModel: Decodable, HasTitle {
var title: String?
}
func checkProperties<T:HasTitle>(model:Any,mod:T.Type){
typealias MMM = T
let title = (model as! MMM).title
print("type is \(title)")
}
EDIT
This makes usage of a concept called Retroactive Modeling. The rationale is separating that property in a protocol, which I called HasTitle. The trick is making an extension to Decodable which has a property title declared the same way you expect in HasTitle protocol. Then, whichever type who conforms to Decodable got that title property by default and you need just to declare HasTitle conformance. However, you're still free to override it title property.
I was trying to define a protocol with some property of type AnyObject, then in the class that conforms to the protocol the property type is SomeClass. However this returned a compiler error. I had to change the type in the class to AnyObject. How can I use the super class in protocol definition and use the sub class as property type?
Thanks!
protocol TestProtocol {
var prop: [AnyObject] {get}
}
class Test: TestProtocol {
var prop = [SomeClass]() //compiler error
var prop = [AnyObject]() //this will work
}
The array is an unnecessary complication, so let's remove it and just think about a simple type. This is not legal:
protocol TestProtocol {
var prop: AnyObject {get}
}
class SomeClass {}
class Test: TestProtocol {
var prop : SomeClass = SomeClass() // error
}
The problem is that the protocol declaration says quite literally that whoever claims to adopt TestProtocol must have a property prop of type AnyObject — not of some type that merely conforms to AnyObject.
If you find this surprising, you may be confusing instances of types with types themselves. It is true that an instance of SomeClass can be used where an instance of AnyObject is expected. But types themselves do not work this way; you cannot substitute the type SomeClass when the protocol demands the type AnyObject.
To see this more clearly, note that this does compile just fine:
protocol TestProtocol {
var prop: AnyObject {get}
}
class SomeClass {}
class Test: TestProtocol {
var prop : AnyObject = SomeClass() // changing the declared _type_
}
That compiles, as you already discovered; but, as you have also said, it is not what you wanted. So how to do what you want?
Well, in Swift, the way you express the notion of specifying a type that conforms to a protocol type is through a generic with a constraint. That is what the answers you've been given do. A typealias in a protocol declaration can be a way of making a generic protocol. And a generic can have a constraint, saying e.g. that the type in question should conform to a protocol or inherit from a class. Thus, this is legal and is the sort of solution you're looking for:
protocol TestProtocol {
typealias T:AnyObject // generic
var prop: T {get}
}
class SomeClass {}
class Test: TestProtocol {
var prop : SomeClass = SomeClass() // :)
}
The line typealias T:AnyObject says that T must be a type that conforms to AnyObject, which is precisely what you are trying to say.
A playground example of what you can do:
class SomeClass {
}
class Subclass : SomeClass{
}
protocol TestProtocol {
typealias T : SomeClass
var prop: [T] {get}
}
class Test: TestProtocol {
var prop = [Subclass]()
func test(){
prop.append(Subclass())
}
}
let test = Test()
test.test()
print(test.prop) // prints "[Subclass]\n"
You can avoid pushing the knowledge of the array element class up to the protocol by using a type alias requirement:
protocol TestProtocol
{
typealias ArrayElement: AnyObject
var prop: [ArrayElement] {get}
}
class Test: TestProtocol
{
typealias ArrayElement = SomeClass
var prop:[ArrayElement] = [] //No compiler error
}
Here is how you would do it with associatedtype in Swift 5:
protocol TestProtocol {
associatedtype T: AnyObject
var prop: [T] {get}
}
class SomeClass {}
class Test: TestProtocol {
var prop = [SomeClass]()
}
Note that it works only if your SomeClass is a concrete type. This won't work if there was a protocol type instead. For example:
protocol TestProtocol {
associatedtype T: AnyObject
var prop: [T] {get}
}
protocol SomeProtocol: AnyObject {}
class Test: TestProtocol {
var prop = [SomeProtocol]()
}
I'm dealing with enum and subclassing in Swift.
Each child bring its own new properties which have to be stored in an Enum. This enum is declared with some values in the mother class. I'd like to add some value to this enum. I can't find out how to do it, I tried this without result :
extension MotherClass {
enum Enumeration {
case NewProperty
}
}
The only way to add items to an enum is to add them directly to its declaration. You cannot add more items to an enum through inheritance or any other extension mechanism: the enum must be fully defined at the point of its declaration.
You can use a protocol to solve your problem
protocol MyProtocol {
func code() -> Int
}
then create multiple enums according to your requirement
enum MyEnum:Int, MyProtocol {
func code() -> Int {
return self.rawValue
}
case ok1 = 0
case other1
}
and another enum
enum MyEnum2:Int, MyProtocol {
func code() -> Int {
return self.rawValue
}
case ok2 = 10
case other2
}
Then use MyProtocol where ever you want to get enum
struct MyStruct {
var test1: MyProtocol
var test2: MyProtocol
}
class MyClass {
var abc = MyStruct(test1: MyEnum1.ok1, MyEnum2.ok2)
}