Calling class properties from within protocol extension - ios

In using protocol extension for default implementation I encounter a problem. I define a protocol with different optional properties and functions to use. In the extension I implement a default function and nil properties. In the implementation I use properties and functions from within the protocol.
On the device it works how I expect. Also on the debugger it gave me the property from the class when I run into breakpoint within the extension implementation.
Can someone help me out why I didn't get the property in the example from the class but the nil property from extension.
Example from playground
import UIKit
protocol Collectable {
var item: String? { get }
func collect()
}
extension Collectable {
var item: String? { return nil }
func collect() {
if let some = item {
print("collect \(some)")
} else {
print("nothing")
}
}
}
class Car: Collectable {
var item = "letter"
}
let car = Car()
car.collect()

In the protocol your item is optional string, while in your class you declared another variable named also item. Your collect function is looking for the optional item, and in the extension you specified that it should return nil.

As #SavcaMarin stated in their answer, you have a type conflict between the item property declared in your extension (String?) and the one in your Car (String).
You can see this if you comment the extension. The compiler will tell you that Car does not conform to Collectable. If you ask it to create the required stubs it will create func collect and var item: String?. It then gives an error that item is re-defined.
As Rob and Matt pointed out in comments on the other answer, this seems to be a bug in the Swift compiler. It doesn't indicate the re-definition of item or the non-conformance of your item to the protocol if there is a default implementation that satisfies the protocol conformance (your extension).
The simple fix is to tell Swift that the item in Car is the same as item in Collectable by declaring the type explicitly (String?) rather than relying on type inference (Which gives String):
class Car: Collectable {
var item:String? = "letter"
}

Related

Dependency injection with associated types causing arguments without type names (undescores) in Swift

The situation is following: I'm using a protocol to inject dependencies and the best way I found to implement this in Swift is to use the associatedtype keyword. I am also using protocol composition since some implementations of TestProtocol need more than one dependency.
protocol TestProtocol: class {
associatedtype Dependencies
func inject(_ dependency: Dependencies)
}
protocol HasSomething {
var something: Something { get set }
}
protocol HasSomethingElse {
var somethingElse: SomethingElse { get set }
}
To use this I found that I'll need to use generics like this:
class TestService<T> where T: TestProtocol, T.Dependencies == TestService {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
self.testProtocol?.inject(self)
}
}
Now when I want to use this service somewhere else and I'm trying to initiate it I get following problem:
The parameter is displayed as _ and not as the protocol name TestProtocol.
Let's say I would use this code in a library. How would a user know (without reading the documentation of course) what type could be used in this context when he is not even knowing what protocol he has to implement?
Is there a better way on how to use dependency injection with the type actually being displayed to the user, or am I doing something wrong in the where clause of the TestService class, or is this simply not possible in the current versions of Swift?
There is nothing wrong with your code, this is simply not possible.
class TestService<T> where T: TestProtocol
The where clause means T could be anything, with the constraint that the given object must conform to TestProtocol.
The Xcode autocomplete feature only displays the resolved type when available, but it doesn't show the constraints on a generic, and unfortunately there is nothing you can do about that.
You have the exact same issue in the swift standard library, with Dictionary for example
public struct Dictionary<Key, Value> where Key : Hashable {
public init(dictionaryLiteral elements: (Key, Value)...) {
// ..
}
}
The generic Key as a constraint to Hashable, but Xcode still shows _ in the autocomplete list.
I guess Swift developers are use to this behaviour, so it won't be a big issue, even if your code is embedded in a library.
How would a user know (without reading the documentation of course) what type could be used in this context when he is not even knowing what protocol he has to implement?
Because Xcode is pretty clear about the protocol requirement.
If I try to initialize the TestService with a String I'll get the error:
Referencing initializer 'init(with:)' on 'TestService' requires that 'String' conform to 'TestProtocol'
Which is pretty self explanatory.
Actually at the time of init(with testProtocol: T) Compiler doesn't know about T of course because it is generic
if you provide directly class it will show you in suggestion
For example
class TestService<T:Something> {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
}
}
Now you will see compiler know that it need SomeThing at T
For your case For TestProtocol You can replace with with something user readable world. for next time compiler will give you provided type as suggestion
For Example
class TestService<T:TestProtocol> {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
}
func add(t:T) {
}
}
class Test {
init() {
let t = Something()
let ts = TestService(with: t)
}
}
In Test class you can type ts.add now it knows

Get only property defined in protocol causes compilation error when modifying inner property of object

Consider code like this:
protocol SomeProtocol {
var something: Bool { get set }
}
class SomeProtocolImplementation: SomeProtocol {
var something: Bool = false {
didSet {
print("something changed!")
}
}
}
protocol MyProtocol {
var myProperty: SomeProtocol { get }
}
class MyClass: MyProtocol {
var myProperty: SomeProtocol = SomeProtocolImplementation() {
didSet {
print("myProperty has changed")
}
}
}
var o: MyProtocol = MyClass()
o.myProperty.something = true
This code doesn't compile with error:
error: cannot assign to property: 'myProperty' is a get-only property
o.myProperty.something = true
~~~~~~~~~~~~ ^
Why? My property is of type of SomeProtocolImplementation, which is class type so it should be possible to modify it's inner property using reference to myProperty.
Going further, after modifying myProperty definition so that it looks like that:
var myProperty: SomeProtocol { get set }
something weird happens. Now the code compile (not a surprise), but the output is:
something changed!
myProperty has changed
So at this point SomeProtocolImplementation starts behaving like a value type - modyifing it's internal state causes that the "didSet" callback for myProperty is triggered. Just as SomeProtocolImplementation would be struct...
I actually find the solution, but I want also understand what's going on. The solution is to modify SomeProtocol definition to:
protocol SomeProtocol: class {
var something: Bool { get set }
}
It works fine, but I'm trying to understand why it behaves like this. Anybody able to explain?
First read what Class Only Protocol is. Concentrate on the note section that says:
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
Above quote should get you the idea.
You are trying to get the behavior of reference type for your SomeProtocol's conforming class (i.e. SomeProtocolImplementation). You want to be able to change the value of something in future. So basically you are directing to the above quoted sentence.
If you need more clarification please consider the following more meaningful design where I changed the naming for convenience:
protocol Base: class {
var referenceTypeProperty: Bool { get set }
// By now you are assuming: this property should be modifiable from any reference.
// So, instantly make the protocol `Class-only`
}
class BaseImplementation: Base {
var referenceTypeProperty: Bool = false {
didSet {
print("referenceTypeProperty did set")
}
}
}
protocol Child {
var valueTypeProperty: Base { get }
// This property shouldn't be modifiable from anywhere.
// So, you don't need to declare the protocol as Class-only
}
class ChildImplementation: Child {
var valueTypeProperty: Base = BaseImplementation() {
didSet {
print("valueTypeProperty did set")
}
}
}
let object: Child = ChildImplementation()
object.valueTypeProperty.referenceTypeProperty = true
Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.
Protocol Apple Documentation
When you try to 'set' value to a variable that is read-only - you are trying to change the protocol's implementation. Conforming classes can only consume information from protocol. In Swift we can write protocol extensions where we can have alternative methods for the protocol.
In short think of computed variables as functions. You are technically trying to change a function in this case.
I actually find the solution, but I want also understand what's going on.
I was just about to tell you to make SomeProtocol a class protocol, but you already figured that out. — So I'm a little confused as to what you don't understand.
You understand about reference types and value types, and you understand about class protocols and nonclass protocols.
Well, as long as SomeProtocol might be adopted by a struct (it's a nonclass protocol), then if you are typing something as a SomeProtocol, it is a value type. The runtime isn't going to switch on reference type behavior just because the adopter turns out to be a class instance; all the decisions must be made at compile time. And at compile time, all the compiler knows is that this thing is a SomeProtocol, whose adopter might be a struct.

Swift cast object to type and protocol at the same time

How can I cast a given object to a type and a protocol in order to call some methods that are defined as an extension
For Example:
extension Identifiable where Self: NSManagedObject, Self: JsonParseDescriptor {
func someMethod() { }
}
Now I have an object that I retrieved from Core data and I would like to cast it to the above protocols in order to call someMethod on it. I could cast to the protocols using protocol<Identifiable, JsonParseDescriptor> , but how can I include the NSManagedObejct type in it also?
Thanks
As of Swift 4, it is now possible to make mentioned cast directly without tricky workarounds. The task is accomplished similarly as we do protocol composition:
var myVar = otherVar as! (Type & Protocol)
No more need for extensions and bridge protocols.
What you're looking for it called a concrete same-type requirement. Unfortunately, it's not yet possible in Swift.
See ticket SR-1009 and SR-1447 for details. You should also checkout this answer.
In the mean-while, you can extend NSManagedObject with a dummy protocol with the methods you need:
protocol _NSManagedObject {
//the methods you want
}
extension NSManagedObject: _NSManagedObject {}
extension Identifiable where Self: _NSManagedObject, Self: JsonParseDescriptor {
func someMethod() { }
}

Override var conforming to a protocol with a var conforming to a child of the overridden var protocol

This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd

How to define an array of objects conforming to a protocol?

Given:
protocol MyProtocol {
typealias T
var abc: T { get }
}
And a class that implements MyProtocol:
class XYZ: MyProtocol {
typealias T = SomeObject
var abc: T { /* Implementation */ }
}
How can I define an array of objects conforming to MyProtocol?
var list = [MyProtocol]()
Gives (together with a ton of SourceKit crashes) the following error:
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements
Even though the typealias is in fact defined in MyProtocol.
Is there a way to have a list of object conforming to a protocol AND having a generic constraint?
The problem is about using the generics counterpart for protocols, type aliases.
It sounds weird, but if you define a type alias, you cannot use the protocol as a type, which means you cannot declare a variable of that protocol type, a function parameter, etc. And you cannot use it as the generic object of an array.
As the error say, the only usage you can make of it is as a generic constraint (like in class Test<T:ProtocolWithAlias>).
To prove that, just remove the typealias from your protocol (note, this is just to prove, it's not a solution):
protocol MyProtocol {
var abc: Int { get }
}
and modify the rest of your sample code accordingly:
class XYZ: MyProtocol {
var abc: Int { return 32 }
}
var list = [MyProtocol]()
You'll notice that it works.
You are probably more interested in how to solve this problem. I can't think of any elegant solution, just the following 2:
remove the typealias from the protocol and replace T with AnyObject (ugly solution!!)
turn the protocol into a class (but that's not a solution that works in all cases)
but as you may argue, I don't like any of them. The only suggestion I can provide is to rethink of your design and figure out if you can use a different way (i.e. not using typealiased protocol) to achieve the same result.

Resources