title: Swift protocol associated types and inheritance contraints
body: <p>I want to build an iOS app with a <code>UICollectionViewController</code> that always have the same number of cells per row. Because I don't want my <code>UICollectionViewController</code> to deal with too much things, I've refactored my code and implemented funny things like <code>protocol associatedtype</code> and generic types. Now, my app is composed of 4 different <code>.swift</code> files.</p>
  
  <hr>
  
  <h3>1. <code>CustomFlowLayout.swift</code></h3>
  
  <p><code>CustomFlowLayout</code> is a simple subclass of <code>UICollectionViewFlowLayout</code> that allows us to set its <code>minimumInteritemSpacing</code>, <code>minimumLineSpacing</code> and <code>sectionInset</code> properties with dependency injection thanks to a convenience initializer.</p>
  
  <pre><code>import UIKit
  
  class CustomFlowLayout: UICollectionViewFlowLayout {
  
      convenience init(minimumInteritemSpacing: CGFloat = 0,
                       minimumLineSpacing: CGFloat = 0,
                       sectionInset: UIEdgeInsets = .zero) {
          self.init()
          self.minimumInteritemSpacing = minimumInteritemSpacing
          self.minimumLineSpacing = minimumLineSpacing
          self.sectionInset = sectionInset
      }
  
      override init() {
          super.init()
      }
  
      required init?(coder aDecoder: NSCoder) {
          fatalError("init(coder:) has not been implemented")
      }
  
  }
  </code></pre>
  
  <h3>2. <code>ColumnDataSource.swift</code></h3>
  
  <p><code>ColumnDataSource</code> is a subclass of <code>NSObject</code> that conforms to <code>UICollectionViewDataSource</code>, <code>UICollectionViewDelegate</code> and <code>UICollectionViewDelegateFlowLayout</code>. Its implements <code>collectionView(_:layout:sizeForItemAt:)</code> in order to display the correct number of <code>UICollectionViewCell</code>s per row. Also note that <code>ColumnDataSource</code> is a generic class that requires us to pass it a type parameter at initialization.</p>
  
  <pre><code>import UIKit
  
  class ColumnDataSource&lt;FlowLayoutType: UICollectionViewFlowLayout&gt;: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
  
      let cellsPerRow: Int
  
      init(cellsPerRow: Int) {
          self.cellsPerRow = cellsPerRow
          super.init()
      }
  
      // MARK: - UICollectionViewDataSource
  
      func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
          return 10
      }
  
      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
          return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
      }
  
      // MARK: - UICollectionViewDelegateFlowLayout
  
      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -&gt; CGSize {
          let flowLayout = collectionView.collectionViewLayout as! FlowLayoutType
          let marginsAndInsets = flowLayout.sectionInset.left + flowLayout.sectionInset.right + flowLayout.minimumInteritemSpacing * (CGFloat(cellsPerRow) - 1)
          let itemWidth = (collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)
          return CGSize(width: itemWidth, height: itemWidth)
      }
  
  }
  </code></pre>
  
  <h3>3. <code>ColumnFlowLayoutable.swift</code></h3>
  
  <p>The purpose of <code>ColumnFlowLayoutable</code> protocol is to make sure that any class that conforms to it has <code>columnDataSource</code> and <code>customFlowLayout</code> properties where <code>columnDataSource</code> type's type parameter matches <code>customFlowLayout</code> type.  </p>
  
  <pre><code>import UIKit
  
  protocol ColumnFlowLayoutable {
  
      associatedtype FlowLayoutType: UICollectionViewFlowLayout
      var columnDataSource: ColumnDataSource&lt;FlowLayoutType&gt; { get }
      var customFlowLayout: FlowLayoutType { get }
  
  }
  </code></pre>
  
  <h3>4. <code>CollectionViewController.swift</code></h3>
  
  <p><code>CollectionViewController</code> is a subclass of <code>UICollectionViewController</code> that conforms to <code>ColumnFlowLayoutable</code> protocol. It also implement <code>viewWillTransition(to:with:)</code> in order to deal with container size changes.</p>
  
  <pre><code>import UIKit
  
  class CollectionViewController: UICollectionViewController, ColumnFlowLayoutable {
  
      let columnDataSource = ColumnDataSource&lt;CustomFlowLayout&gt;(cellsPerRow: 2)
      let customFlowLayout = {
          CustomFlowLayout(minimumInteritemSpacing: $0, minimumLineSpacing: $0, sectionInset: UIEdgeInsets(top: $0, left: $0, bottom: $0, right: $0))
      }(10)
  
      override func viewDidLoad() {
          super.viewDidLoad()
  
          collectionView?.collectionViewLayout = customFlowLayout
          collectionView?.dataSource = columnDataSource
          collectionView?.delegate = columnDataSource
      }
  
      override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
          super.viewWillTransition(to: size, with: coordinator)
          collectionView?.collectionViewLayout.invalidateLayout()
      }
  
  }
  </code></pre>
  
  <p>The complete project can be found at this Github repo: <a href="https://github.com/imanoupetit/CollectionViewColumnsProtocol" rel="nofollow noreferrer">CollectionViewColumnsProtocol</a>.</p>
  
  <hr>
  
  <p>This code works fine. I can use it with subclasses of <code>CustomFlowLayout</code> and it still works. <strong>However, I cannot use it with subclasses of <code>ColumnDataSource</code></strong>.</p>
  
  <p>If I try to build the project by using a subclass of <code>ColumnDataSource</code> (e.g <code>class SubColumnDataSource: ColumnDataSource&lt;CustomFlowLayout&gt;</code>) in <code>CollectionViewController</code>, Xcode throws the following build time error message:</p>
  
  <blockquote>
    <p>Type 'CollectionViewController' does not conform to protocol 'ColumnFlowLayoutable'</p>
  </blockquote>
  
  <p>What would I have to change in <code>ColumnFlowLayoutable</code> protocol in order to allow <code>CollectionViewController</code> to work with subclasses of <code>ColumnDataSource</code>?</p>