3

We have implemented a protocol, Reusable, to ease UITableView register/dequeue implementations, for our UITableViewCells.

protocol Reusable: class {
    static var defaultIdentifier: String { get }
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }
}

class TestTableViewCell: UITableViewCell, Reusable { }
class AnotherTestTableViewCell: UITableViewCell, Reusable { }

Then, there's an extension to UITableView like:

extension UITableView {
    func register<T: UITableViewCell & Reusable>(_: T.Type) {
        register(UINib(nibName: T.defaultIdentifier, bundle: nil), forCellReuseIdentifier: T.defaultIdentifier)
    }
}

and as its usage:

let tableView: UITableView = UITableView()
tableView.register(TestTableViewCell.self)
tableView.register(AnotherTableViewCell.self)

Everything works well, but we'd like to move these types to an array, for order. That's where we're stuck, this doesn't work:

let viewCells = [TestTableViewCell.self, AnotherTestTableViewCell.self]
// Without type annotation, it's [UITableViewCell.Type]
// And the error is: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

for viewCell in viewCells {
    tableView.register(viewCell)
}

We've also tried:

let viewCells: [Reusable.Type] = ...
// Error: Cannot invoke 'register' with an argument list of type '(Reusable.Type)'

Also this:

let viewCells: [(UITableViewCell & Reusable).Type] = ...
// Error: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

Is there a way to store the class type info with protocol conformance in an array to make this work?

6
  • What's version of Swift are you using? Commented Apr 18, 2019 at 13:05
  • @Vyacheslav 4.2 Commented Apr 18, 2019 at 13:08
  • What's the problem? It doesn't compile? I tried in playground and it compiles Commented Apr 18, 2019 at 13:25
  • @RicoCrescenzio yes, I've edited the question, it's because there's only 1 type of class type in the array. It should not compile. Commented Apr 18, 2019 at 13:30
  • 1
    "but we'd like to move these types to an array” You’re going to have trouble doing that. Swift doesn’t like the notion of an array of metatypes; a metatype is not really a type in Swift. You might be happier using some other programming language, or some other approach to the overall goal. Commented Apr 18, 2019 at 15:40

4 Answers 4

5

Just write an extension for UITableViewCell so it conforms to Reusable protocol and do not cast cells to any type:

extension UITableViewCell: Reusable {}


P.S. you can also check the most popular Reusable protocol implementation on iOS here.


Good luck :)

Sign up to request clarification or add additional context in comments.

9 Comments

It doesn't work unfortunately: Value of type ‘UITableViewCell.Type’ does not conform to ‘UITableViewCell & Reusable’ in coercion
Instead of discussing in comments lets have a chat.
Summary of chat: It works, but we'd really like not to extend the protocol to every UITableViewCell.
@EDUsta Why don't you want to extend every UITableViewCell to conform to the protocol?
@JeremyP Fair question, there are some UITableViewCell classes that doesn't have a separate nib, or doesn't match with their names, so we're not ready to make that transition yet, instead we're doing one-by-one and get to know the process (which cells are ready/not ready).
|
2
protocol Reusable: class {
    static var defaultIdentifier: String { get }
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }
}

class TestTableViewCell: UITableViewCell, Reusable { }
extension UITableView {
    func register<T: UITableViewCell & Reusable>(_: T.Type) {
        register(UINib(nibName: T.defaultIdentifier, bundle: nil), forCellReuseIdentifier: T.defaultIdentifier)
    }
}
let tableView: UITableView = UITableView()
tableView.register(TestTableViewCell.self)
let viewCells = [TestTableViewCell.self]
// Without type annotation, it's [UITableViewCell.Type]
// And the error is: Instance method 'register' requires that 'UITableViewCell' conform to 'Reusable'

for viewCell in viewCells {
    tableView.register(viewCell)
}

This code successfully executes.

2 Comments

Thanks for the answer, I couldn't see any difference, is it about Swift 4.2/5?
Update: When there's only 1 class, it compiles, but if there's more, it doesn't. Updated the question.
1

Hope you are happy with this. The key is Self keyword in protocol.

protocol Reusable: class {
    static var defaultIdentifier: String { get }
    static func register(tableView: UITableView)
}

extension Reusable where Self: UITableViewCell {
    static var defaultIdentifier: String {
        return String(describing: self)
    }

    static func register(tableView: UITableView) {
        tableView.register(UINib(nibName: Self.defaultIdentifier, bundle: nil), forCellReuseIdentifier: Self.defaultIdentifier)
    }
}

extension UITableView {
    func register(_ reusable: Reusable.Type) {
        reusable.register(tableView: self)
    }
}

let viewCells: [Reusable.Type] = [TestTableViewCell.self, AnotherTestTableViewCell.self]

for viewCell in viewCells {
    tableView.register(viewCell)
}

I recommend you to read the following links:

1 Comment

@EDUsta Sounds great. Please accept my answer if you don't mind
0

Simply use this table view extension method to register the cell.

 extension UITableView {
     func registerCell<T: UITableViewCell>(cellType: T.Type) {
        let nib = UINib(nibName: String(describing: cellType.self), bundle: nil)
        let reuseIdentifier = String(describing: cellType.self)
        self.register(nib, forCellReuseIdentifier: reuseIdentifier)
     }
} 

And use it like so

 let tableView: UITableView = UITableView()
    let cellsArray = [Test.self, Test1.self, Test2.self]
    for cell in cellsArray {
        tableView.registerCell(cellType: cell)
    }

1 Comment

Thanks for your response, but it just ignores the protocol and that's the whole reason we're doing this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.