How to avoid adding multiple NSNotification observer?
Asked Answered
E

5

43

Right now the API doesn't seem to provide a way to detect if an observer has already been added for a particular NSNotification. What's the best way to avoid adding multiple NSNotification observers other than maintaining a flag on your end to keep track? Has anyone already created a category to facilitate this?

Evoy answered 14/4, 2011 at 4:9 Comment(1)
Can you give an example of a situation where this might happen, or why this should be a problem?Elaineelam
E
74

One way to prevent duplicate observers from being added is to explicitly call removeObserver for the target / selector before adding it again. I imagine you can add this as a category method:

@interface NSNotificationCenter (UniqueNotif)

- (void)addUniqueObserver:(id)observer selector:(SEL)selector name:(NSString *)name object:(id)object {

        [[NSNotificationCenter defaultCenter] removeObserver:observer name:name object:object];
        [[NSNotificationCenter defaultCenter] addObserver:observer selector:selector name:name object:object];

}

@end

This assumes that the you will only add one unique observer to each target for any notification name, as it will remove any existing observers for that notification name.

Exceptive answered 14/4, 2011 at 7:51 Comment(5)
You're using "observer" and "target" in a very funny, non-standard way here. The "observer" in addObserver... indicates the object that will receive a message when the notification is posted, not the method that will make up that message. There's no notion of "target" in notifications. The thing that you're calling a "target" is referred to in the docs as "observer".Elaineelam
The target variable indicates where the runtime should look for the object. I changed the arg name target to observer to remove any potential confusion.Exceptive
While my "you can't answer" may have been too literal (an object cannot detect it's observers) this answer is at best poor practice. Better advice for the OP would be to use a different implementation other than posting and observing notifications for this use case. If a notification is only intended to have a single observer then using a delegate and protocol not only enforces this but makes it clear in the relationship between the object and its delegate.Chambermaid
@Chambermaid This doesn't have to do with a posted notification going out to one and only one observer. This has to do with an observer not observing the same notification twice. With this method you can still have multiple observers to a notification.Ballista
Yep, I misunderstood the question. This answer does solve the OP's question but feels a bit like a workaround for the root problem - e.g. why is the object observing the same notification multiple times to begin with?Chambermaid
I
20

Swift 5:

import Foundation

extension NotificationCenter {
  func setObserver(_ observer: Any, selector: Selector, name: Notification.Name, object: Any?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}

Swift 3-4:

import Foundation

extension NotificationCenter {
  func setObserver(_ observer: AnyObject, selector: Selector, name: NSNotification.Name, object: AnyObject?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}

Swift 2:

import Foundation

extension NSNotificationCenter {
  func setObserver(observer: AnyObject, selector: Selector, name: String?, object: AnyObject?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}
Intermolecular answered 17/3, 2015 at 12:26 Comment(2)
did you give me example of add observer using this extension in swift 3.0Chloramphenicol
@Fattie removeObserver(self, forKeyPath: tag)? Where did you find this?Intermolecular
G
5

The Upvoted answer with extension NotificationCenter { ... } did not work for me, since my app was creating a new instance of a viewController (this had a Notification observer) every time a notification was posted, so removing an observer on a new instance of a viewController obviously doesn't work. Previous instances of the viewController that had Notification Observers were getting called.

The below worked for me, since this was removing the Notification Observer as soon as the view was being disappeared.

// Notification observer added 

override func viewWillAppear(_ animated: Bool) {

    NotificationCenter.default.addObserver(self, selector: #selector(self.someFunc(notification:)), name: Notification.Name("myNotification"), object: nil)


}


// Notification observer removed 

override func viewWillDisappear(_ animated: Bool) {

    NotificationCenter.default.removeObserver(self, name: Notification.Name("myNotification"), object: nil)


}
Geri answered 27/9, 2017 at 12:33 Comment(3)
better addObserver in viewDidAppear instead of viewWillAppear.Intermolecular
@Intermolecular why? ThanksShorts
because it's the phase of appearing the view controller, thus, if you're don't have the reasonable case, to place it in viewWillAppear (maybe for animation only), it has to be placed in the function viewDidAppear, when the view controller is appeared and it's ready to interact with a user.Intermolecular
J
1

Well should have checked apple documentation.
https://developer.apple.com/documentation/foundation/notificationcenter/1411723-addobserver

let center = NSNotificationCenter.defaultCenter
var tokenOpt: NSObjectProtocol?
tokenOpt = center.addObserverForName("OneTimeNotification", object: nil, queue: nil) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}

So to make sure that the notification is not add if it already existing

if let token = tokenOpt{
  center.removeObserver(token)
}
tokenOpt = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
}
Johnstone answered 3/6, 2021 at 23:10 Comment(0)
S
0

Swift 5:

A couple of tweaks for Swift 5 based on previous answers from @futureelite7 and @dimpiax

extension NotificationCenter {
    func setObserver(_ observer: AnyObject, selector: Selector, name: NSNotification.Name, object: AnyObject?) {
        NotificationCenter.default.removeObserver(observer,
                                                  name: name,
                                                  object: object)
        NotificationCenter.default.addObserver(observer,
                                               selector: selector,
                                               name: name,
                                               object: object)
    }
}
Septempartite answered 1/8, 2021 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.