Dispatching to Swift protocol extension - swift

Why does the following code print "BaseP" at #2?
protocol BaseP { func foo() }
extension BaseP { func foo() { print("BaseP") } }
protocol SubP: BaseP {}
extension SubP { func foo() { print("SubP") } }
class C: SubP {}
let subP1: SubP = C()
subP1.foo() // #1 prints "SubP", fine.
class BaseC: BaseP {}
class SubC: BaseC, SubP {}
let subP2: SubP = SubC()
subP2.foo() // #2 prints "BaseP". why?
In both cases we call foo() on a reference with a static type of SubP,
referencing an object with a dynamic type that is a class conforming to SubP.
Even if it was a static dispatch, I'd think that it should still call SubP.foo().
Why does the base protocol implementation get called at #2?
Also, why does inheriting from BaseC make a difference
(if in the class SubC line I remove BaseC or replace it with BaseP,
then it suddenly prints "SubP")?

When you call subP1.foo() or subP2.foo(), you are calling the foo that satisfies the protocol requirement foo in BaseP. (i.e. the method that witnessed BaseP.foo). There can be only one such witness.
The other important thing is that the foo requirement is not in SubP, but in BaseP. The only requirement of SubP is that the conformer has to also conform to BaseP.
In the case of subP1, C directly conforms to SubP. To resolve the only requirement of SubP, C has to also conform to BaseP. Now the compiler needs to figure which method can witness foo. There are two methods available, but the one from the SubP extension hides the one from the BaseP extension, so the one from the SubP extension is chosen to be the witness, so you see SubP printed.
In the case of subP2, the witness of foo is already decided when you conformed BaseC to BaseP. Here, there is only one choice - foo from the BaseC extension. When you later conformed SubC to SubP, the only requirement left is that SubC should also conform to BaseP. Well, you said SubC inherits from BaseC, so that's fine. The compiler is happy, and foo from the BaseP extension ends up being the witness.

Related

swift subclasses used in generics don't get called when inheriting from NSObject

Partial Solution Update at the end!
Attached is code that produces odd behavior. I copied it out of a swift playground so it should run in one fine.
I created a subclass in my project and passed it to my generic class as the concrete type. However, I quickly noticed that only the base class methods are called. This is shown with myBase and mySub below. Despite the generic class being instantiated as <mySub>, only the base methods are called. The print lines for the subclass are never shown.
Well, I found a simple way around that and that is to not inherit from NSObject. When I used swift native classes, the subclass methods are in fact called. These are secondBase and secondSub.
How do I pass a subclass into a generic class and get the actual subclass to receive calls when inheriting from NSObject?
And why would behavior be different?
import Foundation
// The Protocol
protocol P {
init ()
func doWork() -> String
}
// Generic Class
class G<T: P> {
func doThing() -> String {
let thing = T()
return thing.doWork()
}
}
// NSObject Base Class with Protocol
class A1: NSObject, P {
override required init() {
super.init()
}
func doWork() -> String {
return "A1"
}
}
// NSObject Sub Class
class B1: A1 {
required init() {
super.init()
}
override func doWork() -> String {
return "B1"
}
}
// Swift Base Class
class A2: P {
required init() {
}
func doWork() -> String {
return "A2"
}
}
// Swift Sub Class
class B2: A2 {
required init() {
super.init()
}
override func doWork() -> String {
return "B2"
}
}
print ("Sub class failure with NSObject")
print ("Recieved: " + G<B1>().doThing() + " Expected: B1 - NSObject Sub Class Generic (FAILS)")
print ("\nSub class success with Swift Native")
print ("Recieved: " + G<B2>().doThing() + " Expected: B2 - Swift Sub Class Generic (SUCCEEDS)")
print("")
#if swift(>=5.0)
print("Hello, Swift 5.0")
#elseif swift(>=4.1)
print("Hello, Swift 4.1")
#elseif swift(>=4.0)
print("Hello, Swift 4.0")
#elseif swift(>=3.0)
print("Hello, Swift 3.x")
#else
print("Hello, Swift 2.2")
#endif
Output:
Sub class failure with NSObject
Recieved: A1 Expected: B1 - NSObject Sub Class Generic (FAILS)
Sub class success with Swift Native
Recieved: B2 Expected: B2 - Swift Sub Class Generic (SUCCEEDS)
Hello, Swift 5.0
Partial solution update:
Moving the protocol conformance from the base class to the sub class allows the sub class to behave correctly. Definitions become:
class A1: NSObject
class B1: A1, P
The problem is the base class can no longer be used directly at all when no functionality beyond it is needed. This is mostly an issue if the protocol being conformed to has an associated type. When this is true, you must have a concrete class that conforms to the protocol for use in generics.
One use case here is expecting a base class in the generics (with a protocol involving an associated type) which allows something to function without caring what actual sub class was passed in. This actually ends up being a poor man's form of type erasure in some cases. And you can still use the same generic with the subclass.
G<A1>()
G<B1>()
This was derived from a similar question here: Generic Class does not forward delegate calls to concrete subclass
Partial options are:
remove NSObject and use swift native classes only
when NSObject is required, try to separate protocol conformance from inheritance of NSObject
UPDATE ON THE BELOW IDEA: Doesn't Work
I am going to test if providing an additional layer changes the behavior. Basically have 3 layers, Base class inheriting from NSObject, base Protocol class adding the protocol but inheriting from base and then specific classes. If it can distinguish between the base protocol class and the specific subclass in that case, that would be a functional workaround in all use cases. (and may explain why Apple's NSManagedObject works fine)
Still seems like a bug though.
I was able to confirm your results and submitted it as a bug, https://bugs.swift.org/browse/SR-10617. Turns out this is a known issue! I was informed (by good old Hamish) that I was duplicating https://bugs.swift.org/browse/SR-10285.
In my bug submission, I created a clean compact reduction of your example, suitable for sending to Apple:
protocol P {
init()
func doThing()
}
class Wrapper<T:P> {
func go() {
T().doThing()
}
}
class A : NSObject, P {
required override init() {}
func doThing() {
print("A")
}
}
class B : A {
required override init() {}
override func doThing() {
print("B")
}
}
Wrapper<B>().go()
On Xcode 9.2, we get "B". On Xcode 10.2, we get "A". That alone is enough to warrant a bug report.
In my report I listed three ways to work around the issue, all of which confirm that this is a bug (because none of them should make any difference):
make the generic parameterized type's constraint be A instead of P
or, mark the protocol P as #objc
or, don't have A inherit from NSObject
UPDATE: And it turns out (from Apple's own release notes) there's yet another way:
mark A's init as #nonobjc
This is not an answer so much as a way to avoid the problem.
In most of my code, I did not have to conform to NSObjectProtocol only Equatable and/or Hashable. I have implemented those protocols on the objects that needed it.
I then went through my code, removed all NSObject inheritance except on those classes which inherit from an Apple protocol or object that requires it (Like UITableViewDataSource).
The classes that are required to inherit from NSObject are Generic but they are not typically handed into other Generic classes. Therefore the inheritance works fine. In my MVVM pattern these tend to be the intermediate classes that work with view controllers to make logic like table views reusable. I have a tableController class that conforms to the UITableView protocols and accepts 3 generic viewModel types allowing it to provide the table logic for 95% of my views with no modifications. And when it needs it, subclasses easily provide alternate logic.
This is a better strategy as I am no longer randomly using NSObject for no reason.
This is a second way to avoid the problem.
#matt originally suggested this but then deleted his answer. It is a good way to avoid the problem. His answer was simple. Mark the protocol with objc like this:
// The Protocol
#objc protocol P {
init ()
func doWork() -> String
}
This solves the above sample code and you now get the expected results. But doing this has side effects for swift. At least one of those is here:
How to use #objc protocol with optional and extensions at the same time?
For me, it began a chain of having to make all my protocols objc compatible. That made the change not worth it for my code base. I was also using extensions.
I decided to stay with my original answer at least until Apple fixes this bug or there is a less invasive solution.
I thought this one should be documented in case it helps someone else facing this problem.

Inheritance in Class Vs Protocol

I am little bit messed up with the following concept:
Code 1 :
class New{
func abc(){
print("new class")
}
}
class ClassNew: New {
override func abc() {
print("derived class")
}
}
Code 2:
protocol New{}
extension New{
func abc(){
print("new protocol")
}
}
class ClassNew: New {
func abc() {
print("derived protocol")
}
}
What is the difference between Code 1 and Code 2 as both of them serves the same purpose?
In code 2, classNew is inheriting from new protocol or just conforming to the protocol?
Any Explanation will be highly appreciated!
Code 1 and code 2 are fundamentally not the same.
What is the difference between Code 1 and Code 2 as both of them serves the same purpose?
No they don't. The first one defines a class hierarchy, the second defines a protocol (an API, if you like) and a type that conforms to it.
In code 2, classNew is inheriting from new protocol or just conforming to the protocol?
It's conforming to the protocol. There's no inheritance involved (if you are being pedantic).
Code 1 defines a base class and a class that inherits from it. The subclass overrides the base class for the function abc() and it behaves as you would expect given a class hierarchy i.e.
let x: New = ClassNew()
let y: ClassNew = ClassNew()
print(x.abc()) // prints "derived class"
print(y.abc()) // prints "derived class"
Both print statements call the derived class's abc()
In code 2 you define a protocol with no methods, and an extension to the protocol with an extension method. Note that this is not a "default method" there is nothing in the protocol to default. You then define a class that conforms to the protocol and adds a new method that happens to have the same name as the extension method. The distinction (from the pure class hierarchy) is important because the version of abc() called is determined statically at compile time
protocol New2{}
extension New2{
func abc(){
print("new protocol")
}
}
class ClassNew2: New2 {
func abc() {
print("derived protocol")
}
}
let y2: ClassNew2 = ClassNew2()
let x2: New2 = y2
print(x2.abc()) // prints "new protocol"
print(y2.abc()) // prints "derived protocol"
Even though x2 and y2 are the same object different versions of the function are called. This is because the compiler is not allowed to assume anything about x2 except what it can infer from the protocol. So it doesn't know that the object has anabc() of its own so it must call the extension function.
If you had defined the protocol like this:
protocol New3{
func abc()
}
extension New3{
func abc(){
print("new protocol")
}
}
class ClassNew3: New3 {
func abc() {
print("derived protocol")
}
}
let y3: ClassNew3 = ClassNew3()
let x3: New3 = y3
print(x3.abc()) // prints "derived protocol"
print(y3.abc()) // prints "derived protocol"
This time the compiler knows that the object should have a function abc() and will only use the extension function if it doesn't. Then the normal inheritance rules for classes apply.
Code 2 is an example of protocol extensions. Big difference is that you can't call super.abc() in Code 2 — you have to provide a method implementation that is not in any super context.
Just my opinion — do not use protocol default implementation, as compiler won't save you if you forget to provide "override" when you really need one.
classNew is simply conforming to the protocol in the second code. This is true to every protocol in Swift as inheritance is not currently supported with protocols.
This also answers your first question: there is no significant difference between the two codes in this use case, but, generally speaking, a protocol is good for other kinds of things than subclassing.
Code 1 and Code 2 are exactly same, just conceptually different.
If we put it in simple words, a Class defines the object itself, and a Protocol defines the behaviour of the object.
If compared to Java, a protocol is similar to Interfaces.
Checkout the Protocol Documentation

Generic Constraints and Initializer Inheritance in Swift

I am trying to call an initializer required by protocol A on a type that both conforms to A and is a subclass of C.
All is fine and good if C is a base class. But as soon as C subclasses another class, say B, it breaks.
Here's what I am talking about:
protocol A {
init(foo: String)
}
class B {
init() {}
}
class C: B {}
func makeSomething<T: A>() -> T where T: B {
return T(foo: "Hi")
}
That works. But if I change where T: B to where T: C, I get the following error: argument passed to call that takes no arguments. It will only allow me to call Bs init.
Why can't I call this initializer? I understand that the super class has its own designated initializers that must be called. But that will be enforced when someone actually writes the class that subclasses B and conforms to A. (E.g. implementing init(Foo: String) and calling super.init(bar: Int) inside).
Any help here would be greatly appreciated. Thanks!
Swift provides a default initializer for your base class if it has all properties initialized but not incase of Generics because Swift may think its properties has not been initialized.
So when you constraint your return type as
func makeSomething<T: A>() -> T where T: C
It requires initialization for class C as Swift cannot provide a default initializer.But if your remove the where clause everything works fine
func makeSomething<T: A>() -> T {
return T(foo:"string")
}
If you want to return return T(foo: "Hi") :
You are getting error because class C doesn't have initializer that accepts init(foo: String)
Change your class C as
class C: A {
required init(foo: String) {
}
}
which provides at least one initializer that accepts the argument type.
So, remember if you don't subclass There is already one initializer doing your job and you dont get error.

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

Further constraining a generic function from a Swift Protocol

I have a Swift protocol defined like this:
protocol MyProtocol {
func genericMethod<T:MyProtocol>(param:T) -> ()
}
I can implement the generic method in a base class like this:
class MyBaseClass : MyProtocol {
func genericMethod<T where T:MyProtocol>(param:T) -> () {
println("Performing generic method for type \(T.self)")
}
}
class MySubClass : MyBaseClass {
...
}
So far, so good. I can implement this method and it compiles and runs just fine.
Now, I want to do something similar but in my base class I want to further constrain the type of the generic method by requiring it to conform with a protocol such as Comparable. I try this:
class MyBaseClass : MyProtocol {
func genericMethod<T where T:MyProtocol, T:Comparable>(param:T) -> () {
println("Performing generic method for type \(T.self)")
}
}
Once I add this additional constraint on type T, the class MyClass will not compile because it does not conform to the protocol anymore.
It seems like adding an additional constraint on a generic type should not cause it to cease conforming with a protocol. What am I missing? It seems to me that the protocol is saying that genericMethod must be passed a parameter of a type that conforms with MyProtocol. When I go to implement this in MyBaseClass - just one possible implementation of MyProtocol - that I should be able to restrict that implementation further by saying that the parameter myst conform with Comparable in addition to MyProtocol
Is there a way to refine a generic type in a base implementation like I'm trying to do here?
Adding the additional constraint on a generic type should cause it to cease conforming with the protocol because the protocol is supposed to guarantee conformance, and conformance cannot be guaranteed with subtypes that aren't Comparable. If you want all MyProtocol objects to conform to Comparable then you should make it part of the MyProtocol definition.
protocol MyProtocol: Comparable {
//...
}
I haven't tried this, but it might also work if you make MyBaseClass a Comparable type.
One solution is to go the other way - define your protocol's version of the generic as the most restrictive case. This compiles:
protocol P {
func genericMethod<T where T:P, T:Comparable>(param:T) -> ()
}
class C1 : P {
func genericMethod<T> (param:T) -> () {} // compiles even though omits Comparable
func test() {
genericMethod(C1()) // compiles even though C1 is not a Comparable
}
}