I have a project that uses UIKit. In this project, I have a need to create multiple table views that look the same but represent different contents. Sounds like a great use case for generics! Here’s a basic view controller that achieves this:
import UIKit
class GenericTableViewController<ItemType>: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var tableView: UITableView?
var items: [ItemType] = [] {
didSet {
tableView?.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds)
self.tableView = tableView
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int { 1 }
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
guard section == 0 else { return 0 }
return items.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = String(describing: items[indexPath.row])
return cell
}
}
Now, I’m also using @mattt’s excellent Gist for previewing view controllers. The relevant bit is here:
import SwiftUI
@available(iOS 13.0, *)
struct UIViewControllerPreview<ViewController: UIViewController>: UIViewControllerRepresentable {
let viewController: ViewController
init(_ builder: @escaping () -> ViewController) {
viewController = builder()
}
// MARK: - UIViewControllerRepresentable
func makeUIViewController(context: Context) -> ViewController {
viewController
}
func updateUIViewController(_ uiViewController: ViewController,
context: Context) {
return
}
}
Either one of these works great independently. I can create a sample project with the view controller and use it to display multiple values. However, if I try to use them together, Xcode fails to build the preview with the following error:
extensions of generic classes cannot contain '@objc' members
I’m not sure where the extension comes in, but looking at the diagnostics it looks like my code is being replaced dynamically at runtime with versions that have a __preview__
prefix:
/Users/jeff/Library/Developer/Xcode/DerivedData/GenericViewController-gppxxshymxycwxafswamwxroqimy/Build/Intermediates.noindex/Previews/GenericViewController/Intermediates.noindex/GenericViewController.build/Debug-iphonesimulator/GenericViewController.build/Objects-normal/x86_64/GenericViewController.2.preview-thunk.swift:23:72: error: extensions of generic classes cannot contain '@objc' members
@_dynamicReplacement(for: tableView(_:cellForRowAt:)) private func __preview__tableView(_ tableView: UITableView,
This happens even if I adjust UIViewControllerPreview
to not take a generic ViewController
type and instead operate solely on UIViewController
. Has anyone else run into this—and is there a way to prevent the SwiftUI preview system from dynamically replacing @objc
methods like this in an extension?