0

I have an app which utilizes a NotificationCenter to call a function in viewControllerA when a button is pressed in viewControllerB. It works as intended, kind of. Here is the general setup: ViewControllerA

override func viewDidLoad() {

    NotificationCenter.default.addObserver(self, selector: #selector(self.reloadMainPageVC), name: NSNotification.Name(rawValue: NotificationName.reloadingMainPageVC), object: nil)

}
func reloadMainPageVC(){
    //appends some arrays
    self.tableView.reloadData()
    print("we reloaded viewControllerA")
}

These functions allow for the reloading of viewControllerA's tableview. It can be called by a button press from a button located in viewControllerB.
ViewControllerB

func postNotificationtoReloadMainPageVC() {
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: NotificationName.reloadingMainPageVC), object: nil)
}
@IBAction func buttonTap(_ sender: UIButton) {
    postNotificationReloadMainPageVC()
}

This works in the sense that pressing the button in viewControllerB does indeed reload the table in viewControllerA. Hitting this button the first time reloads viewControllerA and the log reads we reloaded viewControllerA. I now clear the log. Hitting this button again (the second time) will now result in the viewControllerA being reloaded twice (and this causes some duplicates to show up in the table due to some asynchronous appending...) and the log now reads we reloaded viewControllerA we reloaded viewControllerA. I clear the log again. Hitting the button a third time results in viewControllerA being reloaded three times and the log now reads we reloaded viewControllerA we reloaded viewControllerA we reloaded viewControllerA.
The Problem
In case it wasn't obvious, the issue is that regardless of the amount of times I have hit the button, each button hit should result in exactly one reload of the viewControllerA's tableview. However, the 2nd time hitting the button does it twice, the 3rd three times, etc...
What I think the cause is
I do not exactly understand the underlying mechanism of the NotificationCenter, but what my guess (and this is a straight up guess) is, is that the notification is being "posted" every time the button is hit, but any notifications that are already there before the button is hit, is run in addition to the new notification being posted. ie the first time the button is hit, there is no prexisting notification, so it is run once. 0 + 1 = 1. The second time, though, there is already an existing notification, so hitting the button calls 1 (the previous) + 1 (the new) = 2... so it is run twice.
Or maybe that's totally wrong and it is something else?
What I think the solution may be
If my guess at the root of the problem is correct, this can be solved by adding a removal of previously existing notifications from the NotificationCenter:

@IBAction func buttonTap(_ sender: UIButton) {
        //remove previous notifications
        postNotificationReloadMainPageVC()
    }

The issue is that I have no idea what the syntax for that would be. Again, it is totally possible that my assumption was wrong too... in which case... what is the issue and how do I fix it?

Runeaway3
  • 1,439
  • 1
  • 17
  • 43
  • When you're click the button from AVC and it's shows up BVC? Right? – Mannopson Oct 11 '17 at 12:17
  • @Mannopson not exactly sure what you mean. The button is in BVC. The user clicks the button and AVC's table is reloaded... the app does not move to AVC at that point though. At some point later the user will move back to AVC and it will have been reloaded because they clicked the BVC button a while before – Runeaway3 Oct 11 '17 at 12:59
  • I think this is odd. Just use the `reloadData` property inside of `viewDidLoad` when the AVC is loaded. – Mannopson Oct 11 '17 at 13:02
  • Your logic is flawed mate, there is absolutly no need to call reloadData if your tableview is not on the screen. reloadData does not do what you think it does. You need to update your dataSource manually (which is probably an array?) and your tableview will update itself properly on display. – thibaut noah Oct 11 '17 at 14:19
  • @Mannopson I cannot do this because `viewControllerA` and `viewControllerB` are in a tab bar VC. `viewDidLoad()` is not called when switching between the two – Runeaway3 Oct 11 '17 at 18:13
  • @thibautnoah I do update the arrays manually... I glossed over the mechanism a bit, but it is in the question's code as `//appends some arrays`. Doesn't `reloadData()` need to be called after the dataSource is updated? – Runeaway3 Oct 11 '17 at 18:16
  • @Mannopson I guess I could just move the loading function over to `viewDidAppear()` though?... Do you see any issues with this? – Runeaway3 Oct 11 '17 at 18:17
  • So, you'll need to reload the `viewControllers` when they're switched by using `UITabBar`? Right? If so, you'll need a `UITabBarControllerDelegate` method – Mannopson Oct 12 '17 at 04:07
  • @AlekPiasecki reloadData doesn't need to be called because your tableview is not on the screen. When your tableview will be displayed again, your tableview will call cellForRowAtIndex for every visible row. Also you should reload only specific rows if possible (meaning the one on screen who had changes in their datas), calling reloadData is a bad practice, even though sometimes it cannot be avoided. – thibaut noah Oct 13 '17 at 08:43

1 Answers1

0

for anyone having this issue, in the same VC that you add the notification observer, remove the observer in viewWillDissappear

override func viewWillAppear(_ animated: Bool) {
    NotificationCenter.default.addObserver(self, selector: #selector(searchForRecipeTapped(_:)), name: Notification.Name("SearchForRecipeTapped"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: Notification.Name("SearchForRecipeTapped"), object: nil)
}
KMatton
  • 45
  • 4