Search code examples
iosswiftprotocolsswift-protocolsswift-extensions

How to override instance method from protocol extension in Swift?


I'm trying to override an instance method from a protocol extension, and I'm having some trouble.

For context, I'm making an iOS app with a lot of different UICollectionViews. These views get data from different databases (requiring different callback funcs) and have very different layouts. Because any combination of (database, layout) is possible, it's difficult to make a nice OOP class hierarchy without massive code duplication.

I had the idea to put the layout functions (mostly those defined in the UICollectionViewDelegateFlowLayout protocol) into protocol extensions, so I can decorate a given UICollectionView subclass with a protocol that's extended to implement all relevant layout functions, but I'm having a hard time of it. The essence of the problem is contained in the code below.

class Base {
    func speak(){
        print("Base")
    }
}

class SubA: Base, ProtocolA {}

class SubB: Base, MyProtocolB {}

protocol MyProtocolA{
    func speak()
}

protocol MyProtocolB{
    func speak()
}

extension MyProtocolA{
    func speak(){
        print("A")        
    }
}

extension MyProtocolA{
    func speak(){
        print("B")        
    }
}

let suba = SubA()
suba.speak()  // prints "Base", I want it to print "A"

let subb = SubB()
subb.speak()  // prints "Base", I want it to print "B"

Thoughts?


Solution

  • The default implementations in the protocols are only called if the class that conforms to these protocols do not implement that method itself. The classes' methods override the default implementations of the protocols, not the other way around.

    Typically, you'd do something like:

    protocol MyProtocolA {
        func speak()
    }
    
    protocol MyProtocolB {
        func speak()
    }
    
    extension MyProtocolA {
        func speak() {
            print("A")        
        }
    }
    
    extension MyProtocolB {
        func speak() {
            print("B")        
        }
    }
    
    class SubA: MyProtocolA {}
    
    class SubB: MyProtocolB {}
    
    let suba = SubA()
    suba.speak()  // prints "A"
    
    let subb = SubB()
    subb.speak()  // prints "B"
    

    But if you do

    class SubC: MyProtocolA {
        func speak (){
            print("C")
        }
    }
    
    let subc = SubC()
    subc.speak()  // prints "C"
    

    Frankly, as you look at this, the use of Base is entirely redundant in this example, so I've removed it. Clearly, if you need to subclass from Base for other reasons, feel free. But the key point is that protocol default implementations don't override the classes' implementation, but rather the other way around.