I have a single view controller class that I reuse on multiple screens, on different screens I want to supply a different data source. My VC looks something like this:
class SpotViewController: UIViewController {
var dataSource: SpotDataSource!
}
Now I want to have two different Data Source Classes
class PersonalSpotDataSource {}
class ExploreSpotDataSource {}
Now I want to create some sort of a shared protocol that makes the above two classes implmenent the following properties
var spots: [Spot]
var title: String
And then I want to that shared construct to conform UITableViewDataSource since it has everything it needs which is just the list of spots and the title. And then going back to my first class I can supply either class (Personal or explore) as my VC's data source.
Is this possible?
So, first define your protocol:
protocol SpotDataSource {
var spots: [Spot] { get }
var title: String { get }
}
Then define your two classes to conform to that protocol:
class PersonalSpotDataSource: SpotDataSource {
let spots = [Spot(name: "foo"), Spot(name: "bar")]
let title = "Foobar"
}
class ExploreSpotDataSource: SpotDataSource {
let spots = [Spot(name: "baz"), Spot(name: "qux")]
let title = "Bazqux"
}
Clearly, your implementations of these will be more sophisticated than the above, but this illustrates the basic idea.
Anyway, having done that, define a UITableViewDataSource
that uses this protocol:
class SpotTableViewDataSource: NSObject {
let spotDataSource: SpotDataSource
init(spotDataSource: SpotDataSource) {
self.spotDataSource = spotDataSource
super.init()
}
}
extension SpotTableViewDataSource: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return spotDataSource.spots.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SpotCell", for: indexPath) as! SpotCell
cell.spotLabel.text = spotDataSource.spots[indexPath.row].name
return cell
}
}
Finally, define you table view controller to use this UITableViewDataSource
, passing it whatever SpotDataSource
you want:
class PersonalSpotTableViewController: UITableViewController {
private let dataSource = SpotTableViewDataSource(spotDataSource: PersonalSpotDataSource())
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = dataSource
}
}
Note, in that table view controller class, I defined the data source to be stored property because table view controllers don't keep strong reference to their data sources and/or delegates, but we want it to stay around.
Obviously, it would have been more graceful if we could have just put the UITableViewDataSource
methods inside the SpotDataSource
protocol's default implementation, but sadly we can't. So you have to create a concrete object that conforms to UITableViewDataSource
and uses your SpotDataSource
protocol, like outlined above.