How to avoid adding multiple NSNotification observer?

25,849

Solution 1

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.

Solution 2

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)
  }
}

Solution 3

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)


}

Solution 4

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!")
}
Share:
25,849

Related videos on Youtube

Boon
Author by

Boon

I manage a team that builds highly-innovative interactive apps and games for mobile and web. I love building things that reach a lot of people make people happy

Updated on October 21, 2021

Comments

  • Boon
    Boon over 2 years

    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?

  • jscs
    jscs about 13 years
    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".
  • futureelite7
    futureelite7 about 13 years
    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.
  • XJones
    XJones about 8 years
    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.
  • SirRupertIII
    SirRupertIII almost 8 years
    @XJones 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.
  • XJones
    XJones almost 8 years
    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?
  • dimpiax
    dimpiax over 6 years
    better addObserver in viewDidAppear instead of viewWillAppear.
  • Himanshu Moradiya
    Himanshu Moradiya over 6 years
    did you give me example of add observer using this extension in swift 3.0
  • dimpiax
    dimpiax about 4 years
    @Fattie removeObserver(self, forKeyPath: tag)? Where did you find this?
  • Dani
    Dani over 2 years
    @dimpiax why? Thanks
  • dimpiax
    dimpiax over 2 years
    @DanielSpringer 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.

Related