44

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?

Shebuka
  • 3,148
  • 1
  • 26
  • 43
Boon
  • 40,656
  • 60
  • 209
  • 315
  • Can you give an example of a situation where this might happen, or why this should be a problem? – jscs Apr 14 '11 at 06:00

5 Answers5

73

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.

futureelite7
  • 11,462
  • 10
  • 53
  • 87
  • 1
    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". – jscs Apr 14 '11 at 08:05
  • 3
    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. – futureelite7 Apr 14 '11 at 09:04
  • 1
    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. – XJones Feb 27 '16 at 22:55
  • 6
    @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. – SirRupertIII Apr 28 '16 at 20:49
  • 2
    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? – XJones Jun 15 '16 at 17:34
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)
  }
}
dimpiax
  • 12,093
  • 5
  • 62
  • 45
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)


}
SpaceX
  • 2,814
  • 2
  • 42
  • 68
  • 2
    better addObserver in `viewDidAppear` instead of `viewWillAppear`. – dimpiax Oct 12 '17 at 11:54
  • @dimpiax why? Thanks –  Oct 19 '21 at 23:06
  • 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. – dimpiax Oct 21 '21 at 10:00
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!")
}
Muhammad Asyraf
  • 1,748
  • 15
  • 20
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)
    }
}
nurider
  • 1,555
  • 1
  • 18
  • 21