Search code examples
genericsswift4xcplayground

Make a generic TableView DataSource class


I want to make a generic TableView DataSource class, that contains an array of ConfigureModel:

protocol ConfigureModel {
    associatedtype ModelType
    var name: String { get set }

    var modelDescription: String { get }
}

I also want the TableView cell to be generic:

class ConfigureCell<ConfigureType>: UITableViewCell, Configure {
    func configure<Model: ConfigureModel>(with value: Model) where ConfigureType == Model.ModelType {
        let description = value.modelDescription

        print("ConfigureCell description: \(description)")
    }
}

so I made ConfigureCell adopt the generic Configure protocol:

protocol Configure {
    associatedtype ConfigureType

    func configure<Model: ConfigureModel>(with value: Model) where Model.ModelType == ConfigureType
}

Now I can make a model adopt the ConfigureModel protocol to be able to be used in the ConfigureCell class:

struct Tag {
    ....
}
extension Tag: ConfigureModel {
    typealias ModelType = Tag
}

This works fine. Now to make the ObjectDataSource generic:

class ObjectDataSource<T: ConfigureModel>: NSObject, UITableViewDataSource, UITableViewDelegate {
    var values: [T] = []
    ....
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let item = self.values[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "TagCell", for: indexPath) as! ConfigureCell<T>
        cell.configure(with: item)

and here I have a problem that I have tried for many hours to solve. The last cell.configure(with: item statement Xcode is showing the error: Instance method 'configure(with:)' requires the types 'T' and 'T.T' be equivalent

I understand that I have to make a generic where clause in the class, but I have trouble finding out what that should be.

class ObjectDataSource<T: ConfigureModel>: NSObject, UITableViewDataSource, UITableViewDelegate
    where T == ???? {

I have made a Xcode Playground which works, but the commented out parts doesn't work. You can get it here: GenericDataSource Xcode PlayGround


Solution

  • I also want the TableView cell to be generic

    You simply don't need that. You already have defined the configure(with:) method to be a generic one. No need to make the class itself a generic one.


    Given the above statement, if you're okay with it, here will be your implementation as simple as:

    class ConfigureCell: UITableViewCell {
        func configure<Model: ConfigureModel>(with value: Model) {
            let description = value.modelDescription()
    
            print("ConfigureCell description: \(description)")
        }
    }
    
    class ObjectDataSource<T: ConfigureModel>: NSObject, UITableViewDataSource {
        var values: [T] = []
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return values.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let item = self.values[indexPath.row]
            let cell = tableView.dequeueReusableCell(withIdentifier: "TagCell", for: indexPath) as! ConfigureCell
            cell.configure(with: item)
            return cell
        }
    }