How to Listen for All Notifications Sent to the iOS Nsnotificationcenter's Defaultcenter

How can I listen for all notifications sent to the iOS NSNotificationCenter's defaultCenter?

Use NSNotificationCenter's addObserverForName:object:queue:usingBlock: OR addObserver:selector:name:object: method and pass nil for the name and object.

Example

The following code should do the job:

- (void)dumpNotifications {
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
[notifyCenter addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *notification){
// Explore notification
NSLog(@"Notification found with:"
"\r\n name: %@"
"\r\n object: %@"
"\r\n userInfo: %@",
[notification name],
[notification object],
[notification userInfo]);
}];
}

Docs

Here are the docs on addObserverForName:object:queue:usingBlock:. In particular, see the name and obj parameters.

addObserverForName:object:queue:usingBlock:

Adds an entry to the receiver’s dispatch table with a notification
queue and a block to add to the queue, and optional criteria:
notification name and sender.

- (id)addObserverForName:(NSString *)name object:(id)obj
queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification
*))block

Parameters

name

The name of the notification for which to register the observer; that
is, only notifications with this name are used to add the block to the
operation queue. If you pass nil, the notification center doesn’t use
a notification’s name to decide whether to add the block to the
operation queue.

obj

The object whose notifications you want to add the block to the
operation queue. If you pass nil, the notification center doesn’t use
a notification’s sender to decide whether to add the block to the
operation queue.

queue

The operation queue to which block should be added. If you pass nil,
the block is run synchronously on the posting thread.

block

The block to be executed when the notification is received. The block
is copied by the notification center and (the copy) held until the
observer registration is removed. The block takes one argument:

notification

The notification.

Proper way to listen to NSNotifications

Actually, if you pass nil as a name, you'll receive all notifications regardless of their name (not just the two you want). It's better to subscribe to each one separately, by naming them.

Excerpt from Apple documentation:

If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.

(That point was, at first, not clear to me and I misread it by thinking you would not receive any notification).

You can use the same callback/listener for both, and decide what to do based on the notification received.

You could create a category on NSNotificationCenter to handle the registration of multiple names, that's what categories are made for!

NSNotificationCenter addObserver in Swift

It's the same as the Objective-C API, but uses Swift's syntax.

Swift 4.2 & Swift 5:

NotificationCenter.default.addObserver(
self,
selector: #selector(self.batteryLevelChanged),
name: UIDevice.batteryLevelDidChangeNotification,
object: nil)

If your observer does not inherit from an Objective-C object, you must prefix your method with @objc in order to use it as a selector.

@objc private func batteryLevelChanged(notification: NSNotification){     
//do stuff using the userInfo property of the notification object
}

See NSNotificationCenter Class Reference, Interacting with Objective-C APIs

How to pass data using NotificationCenter in swift 3.0 and NSNotificationCenter in swift 2.0?

Swift 2.0

Pass info using userInfo which is a optional Dictionary of type [NSObject : AnyObject]?

  let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: imageDataDict)

// Register to receive notification in your class
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: notificationName, object: nil)

// handle notification
func showSpinningWheel(notification: NSNotification) {

if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}

Swift 3.0, 4.0, 5.0 version and above

The userInfo now takes [AnyHashable: Any]? as an argument, which we provide as a dictionary literal in Swift

  let imageDataDict:[String: UIImage] = ["image": image]

// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict)
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// handle notification
// For swift 4.0 and above put @objc attribute in front of function Definition
func showSpinningWheel(_ notification: NSNotification) {

if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}

NOTE: Notification “names” are no longer strings, but are of type Notification.Name, hence why we are using NSNotification.Name(rawValue: "notificationName") and we can extend Notification.Name with our own custom notifications.

extension Notification.Name {
static let myNotification = Notification.Name("myNotification")
}

// and post notification like this
NotificationCenter.default.post(name: .myNotification, object: nil)

Can I get all existing Notification Center messages using Swift?

Yes, you can:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, { (_, observer, name, _, _) in
print("Notification \(name) received")
}, nil, nil, .deliverImmediately)

But be careful, there are a lot of notifications. :-)

Please consider also this SO question.

Send and receive messages through NSNotificationCenter in Objective-C?

@implementation TestClass

- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}

- (id) init
{
self = [super init];
if (!self) return nil;

// Add this instance of TestClass as an observer of the TestNotification.
// We tell the notification center to inform us of "TestNotification"
// notifications using the receiveTestNotification: selector. By
// specifying object:nil, we tell the notification center that we are not
// interested in who posted the notification. If you provided an actual
// object rather than nil, the notification center will only notify you
// when the notification was posted by that particular object.

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveTestNotification:)
name:@"TestNotification"
object:nil];

return self;
}

- (void) receiveTestNotification:(NSNotification *) notification
{
// [notification name] should always be @"TestNotification"
// unless you use this method for observation of other notifications
// as well.

if ([[notification name] isEqualToString:@"TestNotification"])
NSLog (@"Successfully received the test notification!");
}

@end

... somewhere else in another class ...

- (void) someMethod
{

// All instances of TestClass will be notified
[[NSNotificationCenter defaultCenter]
postNotificationName:@"TestNotification"
object:self];

}

How to observe notifications from a different application?

Catalina silently changed the addObserver behavior - you can no longer use a nil value for the name to observe all notifications - you have to pass in a name. This makes discovery of event names more difficult.

Firstly, you have to understand that while the NSDistributedNotificationCenter has the word Notification in it; it's not related. From the About Local Notifications and Remote Notifications, it does state:

Note: Remote notifications and local notifications are not related to broadcast notifications (NSNotificationCenter) or key-value observing notifications.

So, at that point, I'm going to answer from the perspective of NSDistributedNotificationCenter, and not about Remote/Local notifications - you already have a potential solution in the answer you linked for observing the DB file that contains a record of the notifications in that way.

This example code will not work on Catalina (10.15) because of API behavior changes

The first thing you need to do is listen for the correct notification. Creating a simple application that listens for all events; e.g. using:

NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(notificationEvent:)
name:nil
object:nil];

-(void)notificationEvent:(NSNotification *)notif {
NSLog(@"%@", notif);
}

It indicates that the notifications are:

__CFNotification 0x6100000464b0 {name = com.airserverapp.AudioDidStart; object = com.pratikkumar.airserver-mac; userInfo = {
ip = "2002:d55e:dbb2:1:10e0:1bfb:4e81:b481";
remote = YES;
}}
__CFNotification 0x618000042790 {name = com.airserverapp.AudioDidStop; object = com.pratikkumar.airserver-mac; userInfo = {
ip = "2002:d55e:dbb2:1:10e0:1bfb:4e81:b481";
remote = YES;
}}

This indicates that the name parameter in the addObserver call should be com.airserverapp.AudioDidStart and com.airserverapp.AudioDidStop.

You can use code like that to determine all notifications, which will allow you to add the relevant observers when you want the particular observer. This is probably the easiest way to observe notifications of this type.

App Changed Notifications in CFRunLoop Daemon

Silly mistake!

NSNotificationCenter* center = [NSNotificationCenter defaultCenter];

Should be:

NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter]

NSNotificationCenter posting notifications doesn't work

Reason: your C function TestNWPathMonitor() allocates NotificationReceiver *notification_receiver but the object created is nowhere stored when you leave the scope. So with ARC memory management the object will be released when the scopes block is left, aka its stack is "empty" again.

your monitor aka typedef NSObject<OS_nw_path_monitor> *nw_path_monitor_t; seems to be a global so it will still exist after leaving the scope giving you possibly the misconception that same would be the case for objc allocations, well yes and no. Same would have happend to monitor if it would have been a local variable.

Debugging: Observering [NSNotificationCenter defaultCenter] allows you to catch Notifications almost anywhere in your code, no matter what thread you wait for them, its a NSString based API for good reason with all its pro's and cons. Because of that easy approach it can be hard to find why it is not working. But basically placing an Observer in main.m or APPDelegate should always tell you if it is properly working on the posting side to be sure you did not miss-spell the NotificationName. To avoid the latter case we often declare

extern NotificationName const kSomeNiceNotification;
// in .h && the following in .m
NotificationName const kSomeNiceNotification = @"kSomeNiceNotification";

and use this global key instead as name.

Hint: you can also create a single time Notification that will be triggered and destroy itself when received with other implications you have to think of when doing so. like so (from the Xcode docs)..

NSNotificationCenter * __weak center = [NSNotificationCenter defaultCenter];
id __block token = [center addObserverForName:@"OneTimeNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(@"Received the notification!");
[center removeObserver:token];
}];

See [NSOperationQueue mainQueue] in above code snippet?
You could pass nil there but then the notifications block would execute on the thread the notification was send. When used in UI code, which most often is the usecase for Notifications this is important as UI tasks needs to be executed on the main thread where passing nil forces you later on to wrap UI stuff in

dispatch_async(dispatch_get_main_queue(), ^{ /* UI code block */ }); // or
dispatch_sync(dispatch_get_main_queue(), ^{ /* UI code block */ });

which you don't need to do when you told the notification observer where to execute the notifications block when received.

Ps: in objc we start methodnames in small letters, you will run in trouble when setter & getters violate the "camelCased" methodname rule because the interface
@property NSObject *someName; becomes
-(NSObject*)someName; as getter and -(void)setSomeName:(NSObject*)somename; as setter with modern objc. This tells also why we use the lower underscore to mark local class variables that are the counterparts of almost any property.. in this given example the property NSObject *someName would have an internal _someName counterpart. Not going deeper here as in oldschool objc there is more to know about the class declarations @dynamic ... & @synthesize ... that allow more detailed control of the (internal) local class variable name.

Why to bother about that? Your NotificationReceiver *notification_receiver could override a class property with the same name giving you the impression you made everything right but is still not working as the declaration would still leave the stack empty then. So declaring the variable like _notification_receiver = ... in the methods/function block would make very clear you meant the internal counterpart of its @property NotificationReceiver *notification_receiver; and not a extra local variable.



Related Topics



Leave a reply



Submit