Search code examples
iosswiftuiviewprotocols

How to override swift protocol functions for subclasses (e.g. UILabel from UIView)


I'm trying to implement an extended function that should differ based on what type of class is using it. The objects need to be UIView (or subclass). It should always use function extended on the specified type, but if it doesn't conform to any of them they should use the UIView method instead (as a fallback).

Here is an example of what I'm trying to do:

protocol aProtocol {
    typealias completionBlock = (_ finished:Bool)->()
    func doSomething(completion: completionBlock)
}


extension UIView: aProtocol {
    func doSomething(completion: (Bool) -> ()) {
        print("Im an UIView")
    }
}

extension aProtocol where Self: UILabel {
    func doSomething(completion: (Bool) -> ()) {
        print("im an UILabel")
    }
}

extension aProtocol where Self: UIImageView {
    func doSomething(completion: (Bool) -> ()) {
        print("im an UIImageView")
    }
}

Excecute:

UIView().doSomething { (foo) in } // Should print "Im an UIView"
UIButton().doSomething { (foo) in } // Should print "Im an UIView" (UIButton doesent have specified extended function so should fall back on the UIView function)
UILabel().doSomething { (foo) in } // Should print "im an UILabel"
UIImageView().doSomething { (foo) in } // Should print "im an UIImageView"

Which now prints:

Im an UIView
Im an UIView
Im an UIView
Im an UIView

This means that it always uses the UIView method, even though I want it to use it's own methods. My goal is so it prints:

Im an UIView
Im an UIView
im an UILabel
im an UIImageView

Solution

  • You can achieve that as below, You just need to expose aProtocol to Objective-c runtime for overriding its methods in the extension.

    @objc protocol aProtocol {
        typealias completionBlock = (_ finished:Bool)->()
        func doSomething(completion: completionBlock)
    }
    
    extension UIView: aProtocol {
        func doSomething(completion: (Bool) -> ()) {
            print("Im an UIView")
        }
    }
    
    extension UILabel {
        override func doSomething(completion: (Bool) -> ()) {
            // you can call super.doSomething(completion: completion)
            print("im an UILabel")
        }
    }
    
    extension UIImageView {
        override func doSomething(completion: (Bool) -> ()) {
            // you can call super.doSomething(completion: completion)
            print("im an UIImageView")
        }
    }
    

    Output:

    Im an UIView
    Im an UIView
    im an UILabel
    im an UIImageView