6

I have a fairly big application which has a lot of collection views. Most of the collection view have same implementations for Data Source and the Flow Layout Delegate (same sizes, margins etc). I am trying to create a single protocol which provides the default implementations of UICollectionViewDataSource and UICollectionViewDelegateFlowLayout. Here is my code.

protocol TiledCollectionView{}

extension UICollectionViewDataSource where Self: TiledCollectionView{
    //default implementation of the 3 methods to load the data ...
}
extension UICollectionViewDelegateFlowLayout where Self: TiledCollectionView {
    //default implementation for layout methods to set default margins etc...
}

class MyViewController: UIViewController, TiledCollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
    // the rest of the required logic for view controller
    // here I Don't implement any CollectionView methods since I have provided the default implementation already
}

The problem is that, the compiler complains that MyViewController does not conform to UICollectionViewDataSource. This should not be the case because I am clearly saying that add the default implementations if the type is TiledCollectionView.

Can some one help?

drcocoa
  • 1,155
  • 1
  • 14
  • 23

3 Answers3

6

I know it's not exactly what you asked, I tried - it didn't work. Now looking for possible answer, because had similiar situation. But I can offer you such on option how to hide in your custom protocol all the logic for delegate/dataSource implementation.

class CollectionViewProtocolHandler: NSObject, UICollectionViewDelegate, UICollectionViewDataSource  {

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 0
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        return UICollectionViewCell() // only for test
    }
}

protocol CollectionViewProtocol {
    var handler: CollectionViewProtocolHandler! {get set}
    mutating func useProtocolForCollectionView(collectionView: UICollectionView)
}

extension CollectionViewProtocol {
    mutating func useProtocolForCollectionView(collectionView: UICollectionView) {
        handler = CollectionViewProtocolHandler()
        collectionView.delegate = handler
        collectionView.dataSource = handler
    }
}

class ViewController: UIViewController, CollectionViewProtocol {
    var handler: CollectionViewProtocolHandler! // CollectionViewProtocol convenience

    override func viewDidLoad() {
        super.viewDidLoad()

        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.backgroundColor = .redColor()
        view.addSubview(collectionView)
        var reference = self
        reference.useProtocolForCollectionView(collectionView) // for initialize protocol
    }
}
katleta3000
  • 2,484
  • 1
  • 18
  • 23
  • This seems like a good pattern for adding decorators, like those in Python or Java. Keep adding the protocols which define a specific behavior and a single method call (useProtocolFor....) can add that behavior. – drcocoa Oct 08 '15 at 17:54
  • `@suparngp` Not that all. If you declare a variable in protocol (`var handler: CollectionViewProtocolHandler!`) - you cannot implement it in extension - so you will have to manually add it in your class. At least you can't build your project without it. It would be nice, if Apple adds declaring block to protocol, when you inherit it like in Ruby - so you can see there all the extended variable and functions and have option to override them. – katleta3000 Oct 09 '15 at 05:27
4

I expect the problem is that this is an Objective-C protocol. Objective-C has never heard of a protocol extension. Therefore it has no knowledge that this protocol extension is injecting two functions into MyClass. It can't see them, and so as far as it is concerned, the protocol requirements are not satisfied.

Arsen
  • 10,815
  • 2
  • 34
  • 46
  • 1
    Suppose, you're right, when I try making default implementation for UICollectionViewDataSource, he shows the error that "candidate is not `@objc`, but protocol requires it". When I add `@objc` attribute to protocol functions, it says that members of protocol extension cannot be declared as `@objc`. For example, I can implement UIAlertViewDelegate. – katleta3000 Oct 08 '15 at 10:02
1

To add to, but modify, what katleta3000 answered with, you can restrict a protocol to only apply to a 'class'

CollectionViewProtocol : class

so that you don't need 'useProtocolForCollectionView:' to be mutating

Which then makes it so you don't need that var reference = self and you can just say self.userProtocolForCollectionView(collectionView)

Especially if you only plan on implementing this protocol only with NSObject's or class types (UIViewController, UICollectionView, etc.)

Cory Wilhite
  • 88
  • 2
  • 4