2

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?

Colin McDonnell
  • 897
  • 1
  • 11
  • 17
  • 2
    "I had the idea to put the layout functions (mostly those defined in the UICollectionViewDelegateFlowLayout protocol) into protocol extensions" Forget that idea. Objective-C will never call the implementation from a protocol extension, because Objective-C can't _see_ a protocol extension. It is a Swift-only feature. – matt Jul 26 '16 at 22:24
  • @matt This question isn't about Obj-C though, is it? – Tim Vermeulen Jul 26 '16 at 22:24
  • 2
    @TimVermeulen Yes, it is. It's about Objective-C delegate methods in Swift protocol extensions. – matt Jul 26 '16 at 22:25

1 Answers1

4

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Great answer, thanks Rob. Followup: how would this be implemented then without code reuse? By "this", I mean any 2-variable mix-and-match scenario like we have here (where any subclass needs to implement functionality on the basis of both its database and layout). The functionality doesn't lend itself to hierarchical structure. – Colin McDonnell Jul 26 '16 at 23:50
  • I'm not following you. Can you give a practical example of what you mean? – Rob Jul 26 '16 at 23:58
  • So my app has UICollectionViews that a) have various layouts and b) grab their data from different databases. More specifically, I have an "icon" layout and and "detail" layout, each of which overrides cellForItemAtIndexPath and a lot of the UICollectionViewDelegateFlowLayout methods. However I also have two databases, and different code is needed depending on which database the CollectionView grabs the data from. So any combination of {Database 1, Database2} x {Icon, Detail} sort of needs it own subclass. But you can't really do that without code duplication – Colin McDonnell Jul 27 '16 at 00:23