Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 2 protocol extension not calling overridden method correctly

Tags:

swift

swift2

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”
like image 729
Raul Agrait Avatar asked Aug 03 '15 19:08

Raul Agrait


3 Answers

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.

like image 169
Qbyte Avatar answered Oct 26 '22 19:10

Qbyte


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()
        });
    }
}
like image 36
ReduxDJ Avatar answered Oct 26 '22 18:10

ReduxDJ


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
like image 31
user3441734 Avatar answered Oct 26 '22 18:10

user3441734