Reloading Table Data

I have the following code that will go and provide data for my tableView, but having a hard time updating the table once I add to the model. I am able to print out confirming that I’ve added new stuff, but not reload the table. I’ve tried adding things like tableView.reloadData() to viewWillAppear (even though I understand this to be not a great solution) and also gone as far to create an outlet to the tableView and call reloadData on that. Not having much luck. I am presenting the data in a TableView and then when a user clicks the “+” button that takes me to another tableViewController to add the data. I am presenting this modally and using self.dismissViewControllerAnimated(true, completion: nil) to return me to my original tableViewController where I am hoping to have the table updated with the new row I just added. Where am I going wrong here?

TeamViewController
class TeamViewController: UITableViewController {
// pointer to table
@IBOutlet var table: UITableView!

// reference to team Model
let teams = TeamModel().teamList

override func viewDidLoad() {
    super.viewDidLoad()
}

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return teams.count
    
}

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("teams", forIndexPath: indexPath)

    // Configure the cell...
    let team = teams[indexPath.row]
    cell.textLabel!.text = team.0

    return cell
}

TeamDetailViewController

@IBAction func addTeamToDB(sender: UIBarButtonItem) {
    let newTeamInfo = (teamName.text!, numberOfPlayersSliderValue, periodLengthSliderValue, numberOfPlayersSliderValue)
    
    newTeam.addTeamToDB(newTeamInfo) // this adds data as expected and then returns me back to my table
    
    // return back to nav controller
    self.dismissViewControllerAnimated(true, completion: nil)
}

I have done tutorials around this before where i’ve set DataSources and delegates. But I didn’t do it this time… Once I can connect this I’m going move the storage into CoreData.

First, there’s nothing wrong with having reloadData called in viewWillAppear - absolutely common practice!

Second, what might help is using NSNotificationCenter. The view controller that contains the table view should observe messages posted that indicate the underlying model has changed and it should call reloadData. Your model should post notifications whenever it changes that data. It’s as simple as that!

Once your model is in Core Data, look into NSFetchedResultsController, which calls a delegate function whenever its data changes.

Thanks, I am learning lots of little tricks with it.

I tried this but it didn’t work and I was unsure how to even debug something like this:

Model

func addTeamToDB(newTeam: Team) {
    teamList.append(newTeam)
    NSNotificationCenter.defaultCenter().postNotificationName("reloadTeams", object: nil)
}

TeamViewController

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(TeamViewController.reloadTableData(_:)), name: "reloadTeams", object: nil)
}

func reloadTableData(notification: NSNotification) {
    table.reloadData()
}

#selector(TeamViewController.reloadTableData(_:))

That looks like the problem - you want to call a selector on self, but it looks more like you are trying to call a class function there. If you have a reloadTableData in your TeamViewController just make it

#selector(reloadTableData:)

The self is enough to make suer the function is called on the right instance.

That actually won’t compile for me. It would compile as #selector(reloadTableData(_:)). But again it didn’t update my table :slight_smile:

How do you even begin to debug something like this?As a new to swift/ios. Although not programming

Sorry, yes that’s new in Xcode 7.3! Your selector looks fine as you have it in the last post. Xcode should be verifying that it can find a function matching that selector in self - test that it is identifying the function you think it is by commenting it out and looking for the error. Set a breakpoint in the function and verify that it is being called when the notification is received. Finally, force the notification to be sent - maybe wire up a temporary button that just sends that notification and ensure that you are receiving the notification when it is sent.

The notification seems to work. I commented out the function got an error. I put a breakpoint in this was returned

notification	NSNotification	"reloadTeams"	0x00007ff00861e870
ObjectiveC.NSObject	NSObject	 
NSObject	NSObject	
isa	NSConcreteNotification *	"reloadTeams"	0x00007ff00861e870

I’m assuming that means that reloadTeams is called.

Is it correct or ok that in my ViewController my initial data source is instantiated like so:

// reference to team Model
let teams = TeamModel().teamList

I printed out teams after the table.reloadData() and it only returned my initial instantiated teams. These are dummy rows of data. But I’ve also used print() in the model that adds a new team to the array of tuples and that confirms that data is being added.

I appreciate your help!!

Apparently this is because I have instances of my model running. One in my TeamViewController and a second one in my TeamViewDetailController. I believe Prepare to Segue is the method I need to send the values over. But I’m a little stuck on how.

In my TeamViewController I get reference to the list of teams with let teams = TeamModel().teamList.

In My TeamViewDetailController how to add a property to be updated with a model and the team list.

I’ve added this prepare function in my TeamViewController

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "addTeamSegue"
    {
        if let destinationVC = segue.destinationViewController as? TeamDetailsViewController {
            // send data
            destinationVC.currentTeams = teams // team list
        }
    }
}

But unsure how I would declare a property on the TeamDetailViewController to be updated with the list and secondly how I would set up a property that would allow me to call a TeamModel.swift function to add the input to the new mode.

Thanks @aeberbach for your help with this. I introduce a Singleton patter into my model and managed to update my tableViewController. Final code looked like this, does it look ok? I am trying to write decent swift code that works, rather than just code that works!

TeamModel

class TeamModel {

// create new type alias
typealias Team = (String, Int, Int, Int)

// singelton instance
static let TeamModelSharedInstance = TeamModel()

// current list of teams, optional as initially will be empty
var teamList : [Team?] = [("Man U", 2, 40, 11)]

// adds a new team to the user's list of teams, accepts a type of Team
func addTeamToDB(newTeam: Team) {
    // append new team to current team list
    teamList.append(newTeam)
    // set notification for model change
    NSNotificationCenter.defaultCenter().postNotificationName("reloadTeams", object: nil)
    }
}

TeamViewController

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return TeamModel.TeamModelSharedInstance.teamList.count
   }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("teams", forIndexPath: indexPath)

    // Configure the cell...
    // get teams
    let teams = TeamModel.TeamModelSharedInstance.teamList
    let team = teams[indexPath.row]
    cell.textLabel!.text = team!.0

    return cell
}

TeamDetailsViewController

    @IBAction func addTeamToDB(sender: UIBarButtonItem) {
    let newTeamInfo = (teamName.text!, numberOfPlayersSliderValue, periodLengthSliderValue, numberOfPlayersSliderValue)
    // update model
    TeamModel.TeamModelSharedInstance.addTeamToDB(newTeamInfo)
    
    // return back to nav controller
    self.dismissViewControllerAnimated(true, completion: nil)
}

Hey, sorry for the long wait - I never use UITableViewController myself but it looks like you’re doing everything properly. My one suggestion is to add a function to your cell that configures the cell given the required information. It moves code out of the DataSource and into the cell where it belongs - my way of thinking is that a cell needs to know how to configure and present itself but the DataSource doesn’t. This is especially beneficial when your cells becomes more complicated or when you have multiple kinds of cells being presented in the one table.

1 Like

Thanks @aeberbach for your help!