How to pass object with NSNotificationCenter
Asked Answered
S

5

137

I am trying to pass an object from my app delegate to a notification receiver in another class.

I want to pass integer messageTotal. Right now I have:

In Receiver:

- (void) receiveTestNotification:(NSNotification *) notification
{
    if ([[notification name] isEqualToString:@"TestNotification"])
        NSLog (@"Successfully received the test notification!");
}

- (void)viewDidLoad {
    [super viewDidLoad];

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

In the class that is doing the notification:

[UIApplication sharedApplication].applicationIconBadgeNumber = messageTotal;
[[NSNotificationCenter defaultCenter] postNotificationName:@"eRXReceived" object:self];

But I want to pass the object messageTotal to the other class.

Strychnine answered 25/10, 2011 at 22:27 Comment(1)
for swift 2.0 and swift 3.0 #36911465Embraceor
V
241

You'll have to use the "userInfo" variant and pass a NSDictionary object that contains the messageTotal integer:

NSDictionary* userInfo = @{@"total": @(messageTotal)};

NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:@"eRXReceived" object:self userInfo:userInfo];

On the receiving end you can access the userInfo dictionary as follows:

-(void) receiveTestNotification:(NSNotification*)notification
{
    if ([notification.name isEqualToString:@"TestNotification"])
    {
        NSDictionary* userInfo = notification.userInfo;
        NSNumber* total = (NSNumber*)userInfo[@"total"];
        NSLog (@"Successfully received test notification! %i", total.intValue);
    }
}
Vaunting answered 25/10, 2011 at 22:42 Comment(3)
Thanks, I'm setting messageTotal to a badge on a UIButton, do you know how I can refresh the button with the new badge count? The code to display the image in viewDidLoad is UIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];Strychnine
I am not sure why you need to compare the notification.name. The mapping of the name should be performed when you do the addObserver(). The receiveTestNotification should only be called when observing a specific notification.Maxfield
Johan, in this simple case you are correct, but it is possible to have multiple notifications trigger the same handlerPantia
M
96

Building on the solution provided I thought it might be helpful to show an example passing your own custom data object (which I've referenced here as 'message' as per question).

Class A (sender):

YourDataObject *message = [[YourDataObject alloc] init];
// set your message properties
NSDictionary *dict = [NSDictionary dictionaryWithObject:message forKey:@"message"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationMessageEvent" object:nil userInfo:dict];

Class B (receiver):

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter]
     addObserver:self selector:@selector(triggerAction:) name:@"NotificationMessageEvent" object:nil];
}

#pragma mark - Notification
-(void) triggerAction:(NSNotification *) notification
{
    NSDictionary *dict = notification.userInfo;
    YourDataObject *message = [dict valueForKey:@"message"];
    if (message != nil) {
        // do stuff here with your message data
    }
}
Mechanician answered 21/6, 2014 at 9:43 Comment(6)
why doesn't this answer have more upvotes?! it works perfectly and isn't a hack!Bravo
@Kairos because it isn't designed to use like this. the object param in postNotificationName should meaning the one which send this notification.Cumulus
Yes the object should be passed as an NSDictionary using the userInfo param and the accepted answer above has now been edited to show this.Mechanician
This is very misleading, why does that answer has so many upvotes? This should be deleted. Everyone should use userInfo which was created exactly for this.Fibster
Ok, thanks for the feedback... I've updated the answer to use the userInfo dictionary as the way to pass object's data.Mechanician
@DavidDouglas in my case selector is not firing>Insupportable
F
34

Swift 5

func post() {
    NotificationCenter.default.post(name: Notification.Name("SomeNotificationName"), 
        object: nil, 
        userInfo:["key0": "value", "key1": 1234])
}

func addObservers() {
    NotificationCenter.default.addObserver(self, 
        selector: #selector(someMethod), 
        name: Notification.Name("SomeNotificationName"), 
        object: nil)
}

@objc func someMethod(_ notification: Notification) {
    let info0 = notification.userInfo?["key0"]
    let info1 = notification.userInfo?["key1"]
}

Bonus (that you should definitely do!) :

Replace Notification.Name("SomeNotificationName") with .someNotificationName:

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

Replace "key0" and "key1" with Notification.Key.key0 and Notification.Key.key1:

extension Notification {
  enum Key: String {
    case key0
    case key1
  }
}

Why should I definitely do this ? To avoid costly typo errors, enjoy renaming, enjoy find usage etc...

Filippa answered 28/12, 2016 at 16:31 Comment(2)
Thanks. Apparently extending Notification.Name is possible but not Notification.Key. 'Key' is not a member type of 'Notification'. See here: https://ibb.co/hDQYbd2Salim
Thank you, it seems Key struct has been removed since then. I am updating the answerFilippa
P
27

Swift 2 Version

As @Johan Karlsson pointed out... I was doing it wrong. Here's the proper way to send and receive information with NSNotificationCenter.

First, we look at the initializer for postNotificationName:

init(name name: String,
   object object: AnyObject?,
 userInfo userInfo: [NSObject : AnyObject]?)

source

We'll be passing our information using the userInfo param. The [NSObject : AnyObject] type is a hold-over from Objective-C. So, in Swift land, all we need to do is pass in a Swift dictionary that has keys that are derived from NSObject and values which can be AnyObject.

With that knowledge we create a dictionary which we'll pass into the object parameter:

 var userInfo = [String:String]()
 userInfo["UserName"] = "Dan"
 userInfo["Something"] = "Could be any object including a custom Type."

Then we pass the dictionary into our object parameter.

Sender

NSNotificationCenter.defaultCenter()
    .postNotificationName("myCustomId", object: nil, userInfo: userInfo)

Receiver Class

First we need to make sure our class is observing for the notification

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("btnClicked:"), name: "myCustomId", object: nil)   
}
    

Then we can receive our dictionary:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}
Paripinnate answered 1/10, 2015 at 4:17 Comment(1)
You are actually violating the intended use of the postNotificationName(). But you are not alone. I have seen many developer using the object parameter for sending user objects. The second argument, the object, is reserved for the sender. You should really user the userInfo to send all kind of objects. Otherwise you might encounter random crashes etc.Maxfield
C
6

Swift 5.1 Custom Object/Type

// MARK: - NotificationName
// Extending notification name to avoid string errors.
extension Notification.Name {
    static let yourNotificationName = Notification.Name("yourNotificationName")
}


// MARK: - CustomObject
class YourCustomObject {
    // Any stuffs you would like to set in your custom object as always.
    init() {}
}

// MARK: - Notification Sender Class
class NotificatioSenderClass {

     // Just grab the content of this function and put it to your function responsible for triggering a notification.
    func postNotification(){
        // Note: - This is the important part pass your object instance as object parameter.
        let yourObjectInstance = YourCustomObject()
        NotificationCenter.default.post(name: .yourNotificationName, object: yourObjectInstance)
    }
}

// MARK: -Notification  Receiver class
class NotificationReceiverClass: UIViewController {
    // MARK: - ViewController Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Register your notification listener
        NotificationCenter.default.addObserver(self, selector: #selector(didReceiveNotificationWithCustomObject), name: .yourNotificationName, object: nil)
    }

    // MARK: - Helpers
    @objc private func didReceiveNotificationWithCustomObject(notification: Notification){
        // Important: - Grab your custom object here by casting the notification object.
        guard let yourPassedObject = notification.object as? YourCustomObject else {return}
        // That's it now you can use your custom object
        //
        //

    }
      // MARK: - Deinit
  deinit {
      // Save your memory by releasing notification listener
      NotificationCenter.default.removeObserver(self, name: .yourNotificationName, object: nil)
    }




}

Coverlet answered 4/3, 2020 at 5:0 Comment(4)
Yes, it works using object (in NotificationCenter.default.post call) for passing an object - however, this is not what it is meant for. Apple documentation states, that object is "The object posting the notification.". Use userInfo instead for passing custom data (as suggested by many of the other answers on this page).Exerciser
Hi, @JRV, Thanks for the insights, I used this implementation on my app back then so far no problem with it. Would you mind clarifying what problems or side effects could occur as a result of using my implementation above? It will be very helpful if you can attach supporting test code.Coverlet
As I wrote, it will work, and I have actually done the same myself - until I read Apple's documentation more carefully 🙈. And I would not recommend working against Apple's own documentation as it can lead to unexpected results in a future implementation of NSNotificationCenter. Here is a SO post that concerns your specific question: stackoverflow.com/questions/33635445Exerciser
And here is a post concerning what the object parameter is actually meant for and how to use it: stackoverflow.com/questions/8779220Exerciser

© 2022 - 2024 — McMap. All rights reserved.