Given the following.
protocol EntityType {
var displayString: String { get }
}
extension String: EntityType {
var displayString: String { return self }
}
class GenericListViewController<Entity>: UIViewController, UITableViewDataSource, UITableViewDelegate where Entity: EntityType {
let items: [Entity]
let tableView: UITableView
init(items: [Entity]) {
self.items = items
self.tableView = UITableView()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[indexPath.row].displayString
return cell
}
// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//
// }
}
class StringListViewController: GenericListViewController<String> {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected: \(items[indexPath.row])")
}
}
Why isn't tableView:didSelectRowAt:
being called in the concrete subclass? This works fine for non-generic types or when the parent class has an implementation and the sub-class overrides it. Is this expected behavior, Swift bug or am I missing something?
It does seem like a possible bug. My guess it that it might have something to do with the fact that Swift generics are not visible to the Objective-C runtime, so even though your methods implemented directly in GenericListViewController<Entity>
are being called, there may be some buggy behavior between the Swift and Obj-C runtimes trying to figure out overrides. Definitely worth a bug report.
I will note though, that in strict OOP abstract superclasses generally do not conform to protocols themselves, they simply provide default implementations. It's still up to the concrete subclasses to declare protocol conformance and fill in any missing implementations.
In the case of your code above, your GenericListViewController<Entity>
class should not conform to UITableViewDataSource
or UITableViewDelegate
. It simply provides the default method implementations that would allow a concrete subclass to conform without having to rewrite those method implementations (unless override is desired). Your StringListViewController
should be the one declaring conformance to the two protocols. If you modify your code to do that, it will actually work as expected.
This doesn't change the fact that you have probably discovered a bug in Swift / Obj-C interop, though. I believe that what you have currently should work, although it's not the strict OOP way of handling protocol conformance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With