I've been running into an issue using Swift 2's protocol extensions with default implementations. The basic gist is that I've provided a default implementation of a protocol method which I am overriding in a class that implements the protocol. That protocol extension method is being called from a base class, which is then calling a method which I have overridden in a derived class. The result is that the overridden method is not being called.
I've tried to distill the problem to the smallest possible playground which illustrates the issue below.
protocol CommonTrait: class {
func commonBehavior() -> String
}
extension CommonTrait {
func commonBehavior() -> String {
return "from protocol extension"
}
}
class CommonThing {
func say() -> String {
return "override this"
}
}
class ParentClass: CommonThing, CommonTrait {
override func say() -> String {
return commonBehavior()
}
}
class AnotherParentClass: CommonThing, CommonTrait {
override func say() -> String {
return commonBehavior()
}
}
class ChildClass: ParentClass {
override func say() -> String {
return super.say()
// it works if it calls `commonBehavior` here and not call `super.say()`, but I don't want to do that as there are things in the base class I don't want to have to duplicate here.
}
func commonBehavior() -> String {
return "from child class"
}
}
let child = ChildClass()
child.say() // want to see "from child class" but it's "from protocol extension”
Unfortunately protocols don't have such an dynamic behavior (yet).
But you can do that (with the help of classes) by implementing commonBehavior()
in the ParentClass
and overriding it in the ChildClass
. You also need CommonThing
or another class to conform to CommonTrait
which is then the superclass of ParentClass
:
class CommonThing: CommonTrait {
func say() -> String {
return "override this"
}
}
class ParentClass: CommonThing {
func commonBehavior() -> String {
// calling the protocol extension indirectly from the superclass
return (self as CommonThing).commonBehavior()
}
override func say() -> String {
// if called from ChildClass the overridden function gets called instead
return commonBehavior()
}
}
class AnotherParentClass: CommonThing {
override func say() -> String {
return commonBehavior()
}
}
class ChildClass: ParentClass {
override func say() -> String {
return super.say()
}
// explicitly override the function
override func commonBehavior() -> String {
return "from child class"
}
}
let parent = ParentClass()
parentClass.say() // "from protocol extension"
let child = ChildClass()
child.say() // "from child class"
Since this is only a short solution for your problem I hope it fits in your project.
To simplify my lack thereof understanding what the word "Yet" means in a non-specific error. I figured out, that I can't seem write a function with arguments, in overriding extended function, and the compiler gives me an error like this, but if I write a simple function with no arguments, and make a custom implementation and call it with my overridden "simple function" It works:
import Foundation
import UIKit
extension UIViewController {
func slideInView(direction: Direction = Direction.LEFT, duration: CFTimeInterval = 0.5, closure:()->() ) {
let animation = CABasicAnimation(keyPath: "transform.translation.x")
animation.fromValue = self.view.bounds.width
animation.toValue = 0
animation.duration = 0.3
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = false
UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.view!.layer.addAnimation(animation,forKey:nil);
}, completion: {(finished) -> () in
closure()
});
}
func slide() {
self.slideInView(.LEFT,duration: 0.66) {
print("Slide in Left Complete")
}
}
}
class OtherUIViewController: UIViewController {
override func slide() {
self.slideFromBottom(.BOTTOM,duration: 0.66) {
print("Slide in Bottom Complete")
}
}
func slideFromBottom(direction: Direction = Direction.BOTTOM, duration: CFTimeInterval = 0.5, closure:()->() ) {
let animation = CABasicAnimation(keyPath: "transform.translation.y")
animation.fromValue = self.view.bounds.height
animation.toValue = 0
animation.duration = 0.3
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.view!.layer.addAnimation(animation,forKey:nil);
}, completion: {(finished) -> () in
closure()
});
}
}
that is Swift's behavior. It can be good or bad, depending or your needs. Swift use static dispatch, so which method is called must be known during compilation. There are some advantages, and as usually, some disadvantages. To see, how Swift works at present time, see next very simple example. For me it looks logically ...
protocol P {
func foo()->Void
}
extension P {
func foo()->Void {
print("protocol foo")
}
}
class A:P {
}
class B:A {
func foo() {
print("B foo")
}
}
class C:B {
}
class D: C {
// here the implementation must be overriden,
// due the indirect inheritance from B
override func foo() {
print("D foo")
}
}
let a = A() // a is A type
a.foo() // protocol foo
let b = B() // B is B type
b.foo() // B foo
let p:P = B() // p is protocol P
// compiler is not able to know, what i would like, the dynamicType of p
// can be everything, conforming to protocol P
p.foo() // protocol foo
(p as? A)?.foo() // protocol foo
// casting to B type, I decided, that p is B type
(p as? B)?.foo() // B foo
(p as? D)?.foo() // nothing is printed, becase the result of casting is nil
// here the types are known at compile time
let c = C()
c.foo() // B foo
let d = D()
d.foo() // D foo
let e:C = D()
e.foo() // D foo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With