2 Replies
      Latest reply on Jul 9, 2018 2:15 PM by macuser1984
      macuser1984 Level 1 Level 1 (0 points)

        I'm trying to extend UICollectionViewDataSource to add a method of my own via a protocol extension, but when I override the default implementation in the class that adopts UICollectionViewDataSource, the protocol extension's implementation is still called. Due to this being for some custom UI, I'm having to call the method using the collection view's dataSource property. A simplified version of what I'm trying to do is like this:

         

        // here's where I'm trying to define a method of my own
        extension UICollectionViewDataSource {
            func myMethod() {
                print("myMethod called in default implementation")
            }
        }
        
        class ViewController: UIViewController {
            // yes, I'm deliberately defining a collection view property separately since I need multiple collection views in the same view controller and don't want them to use the entire window size
            var collectionView = UICollectionView(frame: CGRect(x: 20, y: 20, width: 20, height: 20))
            
            override func viewDidLoad() {
                collectionView.dataSource = self
                collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "identifier")
            }
        }
        
        extension ViewController: UICollectionViewDataSource {
            // these 2 methods are only here so the compiler doesn't complain
            func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                return 1
            }
            
            func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                return collectionView.dequeueReusableCell(withReuseIdentifier: "identifier", for: indexPath)
            }
            
            // here's where I'm overriding it with the specific implementation
            func myMethod() {
                print("myMethod called in overridden implementation")
            }
        }
        
        
        // then in my custom UI code (like a UICollectionView subclass), something like this would be called, and this would call the default implementation in the protocol extension
        dataSource?.myMethod()
        

        Of course, if I'm overriding the default protocol implementation, the expection would be that the overridden implementation is what would get called. Why is this happening, and what can be done to get the desired behavior (if possible)?

         

        Thanks!

        • Re: Unusual Behavior in Method in Extension of UICollectionViewDataSource
          QuinceyMorris Level 8 Level 8 (6,050 points)

          It’s actually doing the right thing, although the right thing is not necessarily what you really want.

           

          When you declare a new method in a protocol extension, it’s NOT a protocol requirement. (Protocol requirements are those declared in the main protocol definition ONLY.) Therefore it’s not a “customization point”, and your class’s declaration doesn’t override it in any sense.

           

          When you call the method via a variable or property of type UICollectionViewDataSource, such as “dataSource” in line 36, the function that’s called is chosen based on the static type, which is the protocol type, and hence the protocol extension method is called, not your class’s method.

           

          As alternative, you could declare a new protocol derived from UICollectionViewDataSource, then conform your view controller to this new protocol. You will then need to refer to the data source as something like “(dataSource as? MyProtocol)”.

           

          FWIW no one really likes the current behavior, but it’s probably going to be a while before a better solution is designed and implemented in the Swift language.