class PersonCell: UICollectionViewCell { @IBOutlet weak var firstLabel: UILabel! @IBOutlet weak var secondLabel: UILabel! } struct Person { let firstName: String let lastName: String } protocol PersonsDatasourceProtocol: class { associatedtype Model associatedtype Cell: UICollectionViewCell func didSelect(model: Model) func indexPath(for cell: Cell) -> IndexPath? func reloadUI() } class PersonsDatasource: UICollectionViewDataSource, UICollectionViewDelegate { var models: [Model] = [] // will be loaded from an api weak var delegate: PersonsDatasourceProtocol? // not possible typealias CellConfigurator = (Model, Cell) -> Void private let cellConfigurator: CellConfigurator init(cellConfigurator: @escaping CellConfigurator) { self.cellConfigurator = cellConfigurator } // datasource and delegate methods func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let model = self.models[indexPath.item] delegate?.didSelect(model: model) } } class PersonsViewController: UIViewController, PersonsDatasourceProtocol { typealias Model = Person typealias Cell = PersonCell @IBOutlet weak var collectionView: UICollectionView! let datasource = PersonsDatasource(cellConfigurator: { model, cell in cell.firstLabel.text = model.firstName cell.secondLabel.text = model.lastName }) func didSelect(model: Model) { // do something with the model ... } func indexPath(for cell: Cell) -> IndexPath? { return collectionView.indexPath(for: cell) } func reloadUI() { collectionView.reloadData() } }