nickpharris nickpharris - 3 months ago 25
Swift Question

Dynamic DataSource swap for UITableView in Swift

I am new to iOS/Swift development, and having a problem with making a dynamic swap of DataSource work for a UITableView - note I am not swapping the Delegate, just the DataSource.

I have read other similar questions/responses on Stack Overflow, and not found one that's relevant to my situation. Typically they're about setting the DataSource on "viewDidLoad" (e.g. this one, and this one), whereas my situation is about swapping the DataSource when the user presses a button. The problems in the referenced questions don't exist in my code.

Here's outline of my code. I have the buttonPress method connected to the TouchUpInside event in the storyboard:

class ViewController: UIViewController, UITableViewDelegate {

@IBOutlet weak var tableView: UITableView!

...

@IBAction func buttonPress(sender: UIButton) {

...

self.tableView.dataSource = DummyDataSource()
self.tableView.delegate = self
self.tableView.reloadData()

}

...

}


...and here's my datasource class:

import UIKit

class DummyDataSource: NSObject, UITableViewDataSource {

let names = ["A", "B", "C"]

func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return names.count
}

func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

var cell = tableView.dequeueReusableCellWithIdentifier(simpleTableIdentifier) as UITableViewCell?

if ( cell == nil ) {
cell = UITableViewCell( style: UITableViewCellStyle.Default,
reuseIdentifier: simpleTableIdentifier)
}

cell!.textLabel?.text = names[indexPath.row]
return cell!

}

}


When I press the button, I can see that the pressButton method is being called correctly, but the data doesn't show up in the tableView (no errors - just no data). Any ideas please? Thank you.

Answer

UITableView's dataSource property is either unsafe_unretained or weak, depending on which version of iOS. Either way, as with any other delegate, it doesn't keep a strong reference.

So when you write a line like this:

self.tableView.dataSource = DummyDataSource()

Your newly instantiated DummyDataSource() property doesn't have any strong references pointing to it. It is therefore immediately released by ARC.

We need to keep a strong reference to the data source if we want it to stick around.

My recommendation would be to add a data source property to your view controller which can keep the strong reference. We will also use the didSet of this property to set the table view's data source property and reload its data.

var dataSource: UITableViewDataSource? {
    didSet {
        tableView?.dataSource = dataSource
        tableView?.reloadData()
    }
}

We use optional-chaining to protect against the data source being set before the view is loaded and the tableView property is populated. Otherwise, we will get a fatal error for trying to unwrap nil.

We shouldn't need to be setting the data source property on the table view anywhere else. And the only reason why we should need to called reloadData() anywhere else is if our data source itself can change the data it is representing. However, it is important that reloadData() is called in sync with resetting the dataSource to protect against some likely index-out-of-bound crashes.

Comments