Skip to content

Instantly share code, notes, and snippets.

@joshuadutton
Last active February 15, 2022 10:52
Show Gist options
  • Save joshuadutton/bf674b70c708bc7e8522 to your computer and use it in GitHub Desktop.
Save joshuadutton/bf674b70c708bc7e8522 to your computer and use it in GitHub Desktop.
Generic Collection View and Table View data sources in Swift
import UIKit
protocol CollectionViewCellConfigurable {
typealias ItemType
typealias CellType: UICollectionViewCell
static func reuseIdentifierForIndexPath(indexPath: NSIndexPath) -> String
static func configureCellAtIndexPath(indexPath: NSIndexPath, item: ItemType, cell: CellType)
}
import UIKit
class CollectionViewDataSource<T: IndexPathIndexable, C: CollectionViewCellConfigurable where T.ItemType == C.ItemType>: NSObject, UICollectionViewDataSource {
let data: T
init(data: T) {
self.data = data
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return data.numberOfSections()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.numberOfItemsInSection(section)
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let reuseIdentifier = C.reuseIdentifierForIndexPath(indexPath)
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as? C.CellType else {
fatalError("Cells with reuse identifier \(reuseIdentifier) not of type \(C.CellType.self)")
}
let item = data.objectAtIndexPath(indexPath)
C.configureCellAtIndexPath(indexPath, item: item, cell: cell)
return cell
}
}
import Foundation
protocol IndexPathIndexable {
// associatedType keyword is comming in Swift 2.2
typealias ItemType
func objectAtIndexPath(indexPath: NSIndexPath) -> ItemType
func numberOfSections() -> Int
func numberOfItemsInSection(section: Int) -> Int
}
extension Array: IndexPathIndexable {
func objectAtIndexPath(indexPath: NSIndexPath) -> Element {
return self[indexPath.item]
}
func numberOfSections() -> Int {
return 1
}
func numberOfItemsInSection(section: Int) -> Int {
return count
}
}
import UIKit
protocol TableViewCellConfigurable {
typealias ItemType
typealias CellType: UITableViewCell
static func reuseIdentifierForIndexPath(indexPath: NSIndexPath) -> String
static func configureCellAtIndexPath(indexPath: NSIndexPath, item: ItemType, cell: CellType)
}
import UIKit
class TableViewDataSource<T: IndexPathIndexable, C: TableViewCellConfigurable where T.ItemType == C.ItemType>: NSObject, UITableViewDataSource {
let data: T
init(data: T) {
self.data = data
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return data.numberOfSections()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.numberOfItemsInSection(section)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let reuseIdentifier = C.reuseIdentifierForIndexPath(indexPath)
guard let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as? C.CellType else {
fatalError("Cells with reuse identifier \(reuseIdentifier) not of type \(C.CellType.self)")
}
let item = data.objectAtIndexPath(indexPath)
C.configureCellAtIndexPath(indexPath, item: item, cell: cell)
return cell
}
}
@joshuadutton
Copy link
Author

You use it like this:

Your custom data model object:

struct CustomCellData {
    let title: String
    let detail: String
}

In your view controller:

import UIKit

class ViewController: UIViewController {

    var dataSource: DataSource<Array<CustomCellData>, CustomTableViewCell>?
    @IBOutlet weak var tableView: UITableView!

    let data = [
        CustomCellData(title: "Hi", detail: "CocoaHeads"),
        CustomCellData(title: "Utah", detail: "Devs Rule!")
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = DataSource(data: data)
        tableView.dataSource = dataSource
    }
}

In your custom cell:

import UIKit

class CustomTableViewCell: UITableViewCell, TableViewCellConfigurable {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var detailLabel: UILabel!

    static func reuseIdentifierForIndexPath(indexPath: NSIndexPath) -> String {
        return "Custom"
    }

    static func configureCellAtIndexPath(indexPath: NSIndexPath, item: CustomCellData, cell: CustomTableViewCell) {
        cell.titleLabel.text = item.title
        cell.detailLabel.text = item.detail
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment