Cómo pasar objetos con NSNotificationCenter

129

Estoy tratando de pasar un objeto del delegado de mi aplicación a un receptor de notificaciones en otra clase.

Quiero pasar entero messageTotal. En este momento tengo:

En receptor:

- (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];

En la clase que está haciendo la notificación:

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

Pero quiero pasar el objeto messageTotala la otra clase.

Jon
fuente
para swift 2.0 y swift 3.0 stackoverflow.com/questions/36910965/…
Sahil

Respuestas:

235

Deberá usar la variante "userInfo" y pasar un objeto NSDictionary que contenga el entero entero messageTotal:

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

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

En el extremo receptor puede acceder al diccionario userInfo de la siguiente manera:

-(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);
    }
}
LearnCocos2D
fuente
Gracias, estoy configurando messageTotaluna insignia en un UIButton, ¿sabes cómo puedo actualizar el botón con el nuevo recuento de insignias? El código para mostrar la imagen viewDidLoadesUIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];
Jon
No estoy seguro de por qué necesita comparar la notificación.nombre. La asignación del nombre debe realizarse cuando realiza addObserver (). Se debe llamar a la notificación de prueba solo cuando se observa una notificación específica.
Johan Karlsson
1
Johan, en este caso simple, estás en lo correcto, pero es posible que varias notificaciones activen el mismo controlador
Lytic
93

Sobre la base de la solución proporcionada, pensé que podría ser útil mostrar un ejemplo pasando su propio objeto de datos personalizado (al que he hecho referencia aquí como 'mensaje' según la pregunta).

Clase A (remitente):

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

Clase B (receptor):

- (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
    }
}
David Douglas
fuente
2
¿Por qué esta respuesta no tiene más votos a favor? ¡funciona perfectamente y no es un truco!
Reuben Tanner
44
@Kairos porque no está diseñado para usarse así. el objectparámetro en postNotificationName debe significar el que envía esta notificación.
xi.lin
2
Sí, el objeto debe pasarse como NSDictionary utilizando el userInfoparámetro y la respuesta aceptada anteriormente se ha editado para mostrar esto.
David Douglas
1
Esto es muy engañoso, ¿por qué esa respuesta tiene tantos votos positivos? Esto debería ser eliminado. Todos deberían usar userInfo que fue creado exactamente para esto.
Shinnyx
Ok, gracias por los comentarios ... He actualizado la respuesta para usar el userInfodiccionario como la forma de pasar los datos del objeto.
David Douglas
27

Versión Swift 2

Como señaló @Johan Karlsson ... Lo estaba haciendo mal. Esta es la forma correcta de enviar y recibir información con NSNotificationCenter.

Primero, miramos el inicializador para postNotificationName:

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

fuente

Vamos a pasar nuestra información usando el userInfoparam. El [NSObject : AnyObject]tipo es una retención de Objective-C . Entonces, en Swift land, todo lo que tenemos que hacer es pasar un diccionario Swift que tenga claves derivadas NSObjecty valores que puedan ser AnyObject.

Con ese conocimiento creamos un diccionario que pasaremos al objectparámetro:

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

Luego pasamos el diccionario a nuestro parámetro objeto.

Remitente

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

Clase de receptor

Primero debemos asegurarnos de que nuestra clase esté observando la notificación

override func viewDidLoad() {
    super.viewDidLoad()

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

Entonces podemos recibir nuestro diccionario:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}
Dan Beaulieu
fuente
En realidad, está violando el uso previsto de postNotificationName (). Pero tú no estás solo. He visto a muchos desarrolladores usar el parámetro objeto para enviar objetos de usuario. El segundo argumento, el objeto, está reservado para el remitente. Realmente debería usar userInfo para enviar todo tipo de objetos. De lo contrario, podría encontrar bloqueos aleatorios, etc.
Johan Karlsson
25

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"]
}

Bonificación (¡que definitivamente deberías hacer!):

Reemplazar Notification.Name("SomeNotificationName")con .someNotificationName:

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

Reemplazar "key0"y "key1"con Notification.Key.key0y Notification.Key.key1:

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

¿Por qué debería hacer esto definitivamente? Para evitar costosos errores tipográficos, disfrute de cambiar el nombre, disfrute de encontrar el uso, etc.

frouo
fuente
Gracias. Aparentemente, es posible extender Notification.Name pero no Notification.Key. 'Key' is not a member type of 'Notification'. Ver aquí: https://ibb.co/hDQYbd2
alpennec
Gracias, parece que la Keyestructura se ha eliminado desde entonces. Estoy actualizando la respuesta
frouo
1

Swift 5.1 objeto / tipo personalizado

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




}
Mussa Charles
fuente