Reusable UITableView for Varying Data Input - Swift/Xcode

I have a TableView that I want to reuse for different categories of data (essentially as plugins.. the tableView being a skeleton and being filled with whatever I want it to be). The TableView is filled with different categories of data (and related actions) depending on essentially what ViewController the user came from. I understand how to make it display the various data (just send it whatever array I want it to display), but I can't figure out how I could control the actions for the data at the specific index selected in didSelectRowAtIndexPath.

How could I do this? How could I create an array that has both Strings and executable actions associated with each indices? For example:

arrayOneNames = ["Tigris", "Leo", "Barko"]
arrayOneActions = [displayTheTigers, displayTheCats, displayTheDogs]

If "Leo" is selected in the tableView, then "displayTheCats" is executed. Again, I want each array to be a separate Class that I can use as a plugin, so that I can fill the tableView with whichever Class of data I want it to display and execute, depending on which ViewController the user came from previously. Please answer in Swift.

The usual way to model the data for a simple tableView is as an array of structs, where each struct represents the data for a given row. The usual way to model an action is as a closure. One example would look like this:

struct RowData {
  var title: String
  var action: (() -> Void)

let rows = [
	RowData(title: "Tigris", action: {
	RowData(title: "Leo", action: {

That's the gist of it.

1 Like

There's no need to wrap displayTheTigers() and displayTheCats() in another closure. Instead, do this:

let rows = [
    RowData(title: "Tigris", action: displayTheTigers),
    RowData(title: "Leo", action: displayTheCats)

var action: (() -> Void) should also be changed to var action: () -> Void. The extra parentheses are unecessary.

1 Like

The issue I'm trying to resolve is that I have a couple hundred rows and the result is over 1500 lines of code in my didSelectItemAtIndexPath function, and that's for just ONE category. When finished, I'll have over six categories. Therefore, I'm trying to figure out how I could set up a separate Class for each category, and use that Class as a plugin. Each Class plugin will contain specific lines of code to be executed depending on what row is selected.

For example, let's say I have a View Controller called "MyTableViewController" which contains a UITableView in it called "tableView." If the category of data I choose to displaying in tableView is "Auto," then when a tableView row is selected, I want to call the AutoActionClass (as a plugin) and execute the specific instructions for whatever row was selected.

The other issue I've run into is in the declaration of the actions. Seeing as how displayTheCats and displayTheTigers are functions only located in MyTableViewController, how do I tell the actions that are declared in AutoActionClass to call those functions? Do I simply make those functions "Public" functions in MyTableViewController?

I don't know how to interpret the term "plugin" in this context. What does it mean?

There are different ways of handling this, of course. One way is to make the RowData only hold the title and have a single closure that's a property in your tableViewController that's called when a row is selected.

var rows: [RowData]?
var rowWasSelected: ((IndexPath) -> Void)?

So one type would build the rows array and set it and the rowWasSelected closure on the tableViewController. Then the rowWasSelected closure would be called when the user selects a row and the original type would know what to do for each row.

tableViewController.rowWasSelected = { [weak self] indexPath in
  switch indexPath.row {
    case 0: self?.displayTheTigers()
    case 1: self?.displayTheCats()

There are other ways to package this up using a protocol for your rows and rowWasSelected members or using a delegate that holds those members. All are pretty similar.

1 Like

Thank you!