3

I have a tab bar application and a tab whose view controller is controlled by a UITableView class. I have a class that is used to download data from the server that then saves it to NSDefaults. From there I want the class to be able to update the table view, but am not sure how to access the table view's class to update it.

class updates {

func checkForUpdates() {
    //start on background thread
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [unowned self] in
        //start contacting database:
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        let actualVersion = self.contactForUpdates()
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        if actualVersion > self.defaults.objectForKey("version") as! Int {
            //update data
            if self.downloadUpdates() {
                //update version number
                self.defaults.setObject(actualVersion, forKey: "version")
                //Indicates progress finished
                //self.stopSpinner()
            }
        }//end updates
        else {
            //Indicates progress finished
            //self.stopSpinner()

        }
    }//end background queue
}

}

There are two areas commented //Indicates progress finished where I want to run tableView from the class:

class connectTableVC: UITableViewController {
    ...
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Alec O
  • 1,697
  • 1
  • 18
  • 31

4 Answers4

7

You can use protocol or NSNotificationCenter

Example of NSNotificationCenter :

From the UIViewController that downloads the data, you can post a notification after finishing downloading the data and tell other objects that there is new data:

NSNotificationCenter.defaultCenter().
                    postNotificationName("newDataNotif", object: nil)

And any other object that wants to listen to this notification should register to observe (in your case the UITableViewController):

NSNotificationCenter.defaultCenter().
                     addObserver(self, #selector(shouldReload), 
                     name:"newDataNotif", object: nil)

and then you need to implement the method to be called when receive the notification:

func shouldReload() {
  self.tableView.reloadData()
}

Note that the notification can send data also of type [NSObject : AnyObject]? example:

NSNotificationCenter.defaultCenter().
                     postNotificationName("newDataNotif", 
                     object: nil, userInfo: ["data":[dataArray]])

And the observer will be like:

NSNotificationCenter.defaultCenter().
                     addObserver(self, #selector( shouldReload(_:)),
                     name:"newDataNotif", object: nil)

And the method like:

func shouldReload(notification:NSNotification) {
  println(notification.userInfo)
}

and finally in your deinit you should remove observer :

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}
Alaeddine
  • 6,104
  • 3
  • 28
  • 45
  • 1
    I had trouble implementing the delegates because the update class is called from the UITabController's view controller instead of an individual tabs. This was simple and quick. Thank you. – Alec O May 28 '16 at 00:26
  • I am using this code to update my NSCollectionView. Its working perfectly as wanted but only the data updates, there no change or update in the collectionView. – Jay Mehta Jan 14 '17 at 17:34
7

I think the easiest way to do what you want to do is use a delegate.

protocol UpdateDelegate {
    func didUpdate(sender: Updates)
}

So in your updates class, where you want to refresh the table view you would call the delegates didUpdate function. (You will have to set the delegate before it starts the update for the method to be called.) The weak keyword is important, if it isn't there, neither of the classes will be GC'd, which will lead to memory leaks.

class Updates {
   weak var delegate: UpdateDelegate?

   ...

    //Indicates progress finished
    self.delegate.didUpdate(self)

In your tableView class you would subscribe to that delegate by adding UpdateDelegate to the top of the class

class ConnectTableVC: UITableViewController, UpdateDelegate {
    ...

    let updates = Updates()
    updates.delegate = self

    ...
    func didUpdate(sender: updates) {
       dispatch_async(dispatch_get_main_queue()) {
          self.tableView.reloadData()
       }
    }
 }

I would recommend against using the NSNotificationCenter, the biggest reason is anyone anywhere can listen to the notification (which might be unlikely in your case, but it is still worth noting).

SeanRobinson159
  • 894
  • 10
  • 19
4

For Swift 3

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "newDataNotificationForItemEdit"), object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(self.shouldReload), name: NSNotification.Name(rawValue: "newDataNotificationForItemEdit"), object: nil)

func shouldReload() {
    self.tableView.reloadData()
}
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
Jignesh
  • 310
  • 3
  • 12
2

There are many ways you can do this. The most standard way is to use delegation. Delegation is a very common pattern in iOS, you are using it with UITableViewDataSource. Your view controller is the delegate of the UITableView, meaning it conforms to the UITableViewDataSource protocol. So what you should do is create a protocol for your Updates class like this:

protocol UpdatesDelegate {
    func didFinishUpdates(finished: Bool)
}

Your Updates class would have a delegate property that conforms to this protocol:

class Updates {
    var delegate: UpdatesDelegate?

Then you put this line where the //indicates progress finished comment was:

self.delegate?.didFinishUpdates(finished: true)

Your ConnectTableViewController would conform to the protocol and reload the table view from there:

extension ConnectTableViewController: UpdatesDelegate {
    func didFinishUpdates(finished: Bool) {
        guard finished else {
            // Handle the unfinished state
            return 
        }
        self.tableView.reloadData()
    }
}

Where ever you created your instance of the Updates class, be sure to set the delegate property to your ConnectTableViewController.

P.S. Your classes need to start with an uppercase letter. I've edited your question to reflect this.

Chandler De Angelis
  • 2,646
  • 6
  • 32
  • 45