DKDataSources 1.0.0

DKDataSources 1.0.0

Maintained by Denis Kakačka.



DKDataSources

Version License Platform

DKDataSource is your neat solution to common problems with multiples cell types in TableViews and CollectionViews, also provides elegant solution for if indexPath.section == 0 alike statements in cellForRow, heightForRow etc.

And this is all ViewController code in code of the first screenshot:

lazy var dataSource = DKTableDataSource<CellType>(
    models: [
        DisclosureCellModel(title: "Disclosure 1", action: .action1),
        TextFieldCellModel(title: "TextField 1", placeholder: "Placeholder 1"),
        SwitchCellModel(title: "Switch 1", isOn: true),
        BannerCellModel(imageName: "placeholder"),
        SwitchCellModel(title: "Switch 2", isOn: false),
        BannerCellModel(imageName: "placeholder"),
        DisclosureCellModel(title: "Disclosure 2", action: .action2),
        TextFieldCellModel(title: "TextField 2", placeholder: "Placeholder 2"),
        BannerCellModel(imageName: "placeholder")
    ]
)

// in `viewDidLoad`
dataSource.registerCells(for: tableView)
tableView.dataSource = dataSource

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Installation

DKDataSources is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'DKDataSources'

Usage

When using DKDataSources you need to meet some requirements to make it work. First of all you need enum conforming to DKCellType protocol. This enum will hold all possible cells and additional info for every cell for register and dequeue.

enum CellType: DKCellType {
    case `switch`(SwitchCellModel)

    var info: (cellClass: AnyClass, identifier: String) {
    switch self {
    case .switch:
        return (cellClass: SwitchCell.self, identifier: NSStringFromClass(SwitchCell.self))
    }
}

Every cell needs corresponding model to be configured with. This model needs to inherit from generic class DKCellModel<> where type is your previously created enum so for now its DKCellModel<CellType>. Then you need to override property type and set it to corresponding cell type. In this case CellType.switch has associated value of type SwitchCellModel so we pass there self.

final class SwitchCellModel: DKCellModel<CellType> {
    override var type: CellType { .switch(self) }

    let title: String
    var isOn: Bool

    init(title: String, isOn: Bool) {
    self.title = title
    self.isOn = isOn

    }
}

When you have your CellType and CellModel all you need now is your cell (obviously). Every cell needs to conform DKTableConfigurableCell protocol, this will force you to implement configure which is called in cellForRow, you also need to cast model here to your cell corresponding model so you have access to concrete properties.

extension SwitchCell: DKTableConfigurableCell {

    func configure<T>(with model: T) where T : DKCellModelProtocol {
        guard let model = model as? SwitchCellModel else { return assertionFailure() }
    
        titleLabel.text = model.title
        switchView.isOn = model.isOn
    }
}

You are done now. And this is all you need to do in your controller to have infinite types of cells in it, even in one section.

lazy var dataSource = DKTableDataSource<CellType>(
    models: [
        TextFieldCellModel(title: "TextField 1", placeholder: "Placeholder 1"),
        SwitchCellModel(title: "Switch 1", isOn: true)
    ])

// in `viewDidLoad`
dataSource.registerCells(for: tableView)
tableView.dataSource = dataSource

DKTableDataSource have closures in initializer for all UITableViewDataSource methods, you can do some additional setups there if needed.

lazy var dataSource = DKTableDataSource<CellType>(
    models: [
        SwitchCellModel(title: "Switch 1", isOn: true),
    ],
    
    configureCell: { (model, cell) in
        
        if let cell = cell as? SwitchCell {
            cell.delegate = self
        }
        
        cell.configure(with: model)},
    
    titleForHeaderInSection: { _, section in "Section Header Title" },
    titleForFooterInSection: { _, section in "Section Footer Title" },
    canEditRowAtIndexPath: { _, indexPath in false },
    canMoveRowAtIndexPath: { _, indexPath in false }
)

TableView with more sections

Just use DKSectionedTableViewDataSource<> as below.

lazy var section1DataSource = DKTableDataSource<CellType>(
    models: [
        DisclosureCellModel(title: "Disclosure 1", action: .action1),
        SwitchCellModel(title: "Switch 1", isOn: true),
    ])

lazy var section2DataSource = DKTableDataSource<CellType>(
    models: [
        BannerCellModel(imageName: "placeholder"),
    ])

lazy var dataSource = DKSectionedTableViewDataSource<CellType>(
    dataSources: [section1DataSource, section2DataSource]
)

// in `viewDidLoad`
dataSource.registerCells(for: tableView)
tableView.dataSource = dataSource

DKSectionedTableViewDataSource also provides closures in initializer.

lazy var dataSource = DKSectionedTableViewDataSource<CellType>(
    dataSources: [section1DataSource, section2DataSource, section3DataSource],
    
    configureCell: { (model, cell) in
        
        if let cell = cell as? SwitchCell {
            cell.delegate = self
        }

        cell.configure(with: model)},
    
    sectionIndexTitles: { _ in ["A", "B", "C"] },

    sectionForSectionIndexTitle: { _, title, index in index }
)

Collection View

Every thing works the same as for TableView but you need to use:

  • DKCollectionConfigurableCell instead of DKTableConfigurableCell
  • DKCollectionDataSource instead of DKTableDataSource
  • DKSectionedCollectionDataSource instead of DKSectionedTableViewDataSource

All cases are covered in Example project.

Author

Denis Kakačka, [email protected]

License

DKDataSources is available under the MIT license. See the LICENSE file for more info.