Como passar um objeto com o NSNotificationCenter

129

Estou tentando passar um objeto do meu aplicativo delegado para um receptor de notificação em outra classe.

Eu quero passar inteiro messageTotal. Agora eu tenho:

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

Na classe que está fazendo a notificação:

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

Mas eu quero passar o objeto messageTotalpara a outra classe.

Jon
fonte
para swift 2.0 e swift 3.0 stackoverflow.com/questions/36910965/…
Sahil

Respostas:

235

Você precisará usar a variante "userInfo" e passar um objeto NSDictionary que contém o número inteiro messageTotal:

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

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

No lado de recebimento, você pode acessar o dicionário userInfo da seguinte maneira:

-(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
fonte
Obrigado, estou definindo messageTotalum crachá em um UIButton. Você sabe como atualizar o botão com a nova contagem de crachás? O código para exibir a imagem viewDidLoadéUIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];
Jon
Não sei por que você precisa comparar o notification.name. O mapeamento do nome deve ser realizado quando você faz o addObserver (). O receiveTestNotification deve ser chamado apenas ao observar uma notificação específica.
Johan Karlsson
1
Johan, neste caso simples, você está correto, mas é possível que várias notificações acionem o mesmo manipulador #
194
93

Com base na solução fornecida, pensei que poderia ser útil mostrar um exemplo passando seu próprio objeto de dados personalizado (que referenciei aqui como 'mensagem' conforme a pergunta).

Classe A (remetente):

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

Classe 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
fonte
2
por que essa resposta não tem mais votos positivos ?! funciona perfeitamente e não é um hack!
Reuben Tanner
4
@ Kairos porque não foi projetado para ser usado assim. o objectparâmetro in postNotificationName deve significar aquele que envia esta notificação.
Xi.lin
2
Sim, o objeto deve ser passado como um NSDictionary usando o userInfoparâmetro e a resposta aceita acima foi editada para mostrar isso.
David Douglas
1
Isso é muito enganador, por que essa resposta tem tantos votos positivos? Isso deve ser excluído. Todos devem usar userInfo, que foi criado exatamente para isso.
Shinnyx 19/10/19
Ok, obrigado pelo feedback ... Atualizei a resposta para usar o userInfodicionário como a maneira de passar os dados do objeto.
David Douglas
27

Versão Swift 2

Como @Johan Karlsson apontou ... eu estava fazendo errado. Aqui está a maneira correta de enviar e receber informações com o NSNotificationCenter.

Primeiro, examinamos o inicializador postNotificationName:

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

fonte

Passaremos nossas informações usando o userInfoparâmetro O [NSObject : AnyObject]tipo é uma retenção do Objective-C . Portanto, em terras Swift, tudo o que precisamos fazer é passar em um dicionário Swift que possua chaves NSObjecte valores que possam ser derivados AnyObject.

Com esse conhecimento, criamos um dicionário que passaremos para o objectparâmetro:

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

Em seguida, passamos o dicionário para o nosso parâmetro de objeto.

Remetente

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

Classe do receptor

Primeiro, precisamos garantir que nossa classe esteja observando a notificação

override func viewDidLoad() {
    super.viewDidLoad()

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

Então podemos receber nosso dicionário:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}
Dan Beaulieu
fonte
Você está realmente violando o uso pretendido do postNotificationName (). Mas voce nao esta sozinho. Eu já vi muitos desenvolvedores usando o parâmetro object para enviar objetos de usuário. O segundo argumento, o objeto, é reservado para o remetente. Você realmente deve usar o userInfo para enviar todos os tipos de objetos. Caso contrário, você poderá encontrar falhas aleatórias 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"]
}

Bônus (que você definitivamente deveria fazer!):

Substitua Notification.Name("SomeNotificationName")por .someNotificationName:

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

Substitua "key0"e "key1"por Notification.Key.key0e Notification.Key.key1:

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

Por que eu definitivamente deveria fazer isso? Para evitar erros de digitação dispendiosos, aproveite a renomeação, aproveite o uso do dispositivo etc.

frouo
fonte
Obrigado. Aparentemente, é possível estender Notification.Name, mas não Notification.Key. 'Key' is not a member type of 'Notification'. Veja aqui: https://ibb.co/hDQYbd2
alpennec
Obrigado, parece que o Keystruct foi removido desde então. Estou atualizando a resposta
frouo 10/07/19
1

Objeto / Tipo Personalizado Swift 5.1

// 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
fonte