Come si creano notifiche personalizzate in Swift 3?


Risposte:


32

Potresti anche usare un protocollo per questo

protocol NotificationName {
    var name: Notification.Name { get }
}

extension RawRepresentable where RawValue == String, Self: NotificationName {
    var name: Notification.Name {
        get {
            return Notification.Name(self.rawValue)
        }
    }
}

Quindi definisci i nomi delle notifiche come enumovunque desideri. Per esempio:

class MyClass {
    enum Notifications: String, NotificationName {
        case myNotification
    }
}

E usalo come

NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)

In questo modo i nomi delle notifiche verranno disaccoppiati dalla Fondazione Notification.Name. E dovrai solo modificare il tuo protocollo nel caso in cui l'implementazione per Notification.Namemodifiche.


Questo è esattamente il modo in cui inizialmente pensavo che avrebbe dovuto funzionare: le notifiche dovrebbero essere enumerazioni. Grazie per il trucco!
hexdreamer

Nessun problema! Ho modificato il codice per includere la conformazione dell'estensione a in NotificationNamemodo che la nameproprietà venga aggiunta solo alle enumerazioni conformi al protocollo.
halil_g

Rigorosamente equivalente ma più logico IMO, puoi definire l'estensione su NotificationName (invece di RawRepresentable) in questo modo:extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj

387

C'è un modo più pulito (credo) per ottenerlo

extension Notification.Name {

    static let onSelectedSkin = Notification.Name("on-selected-skin")
}

E poi puoi usarlo in questo modo

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)

2
Sto usando il codice sopra. Questa è una proprietà statica.
Cesar Varela

3
Molto pulito, mi piace molto
Tom Wolters

10
extension NSNotification.Name invece di extension Notification.Name . Altrimenti Swift 3 reclami con'Notification' is ambiguous for type lookup in this context
lluisgh

9
Ottieni il mio voto positivo per aver fatto un errore di battitura nella stringa e quindi dimostrato il valore dei nomi di notifica digitati: P
Dorian Roy

10
Potrebbe valere la pena notare che questo è il metodo suggerito da Apple in WWDC 2016 Session 207 developer.apple.com/videos/play/wwdc2016/207
Leon

36

Notification.post è definito come:

public func post(name aName: NSNotification.Name, object anObject: AnyObject?)

In Objective-C, il nome della notifica è un semplice NSString. In Swift, è definito come NSNotification.Name.

NSNotification.Name è definito come:

public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
    public init(_ rawValue: String)
    public init(rawValue: String)
}

Questo è un po 'strano, dal momento che mi aspetterei che fosse un Enum e non una struttura personalizzata con apparentemente non più vantaggi.

C'è un typealias in Notification for NSNotification.Name:

public typealias Name = NSNotification.Name

La parte confusa è che in Swift esistono sia Notification che NSNotification

Quindi, per definire la tua notifica personalizzata, fai qualcosa come:

public class MyClass {
    static let myNotification = Notification.Name("myNotification")
}

Quindi chiamarlo:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)

3
Buona risposta. Alcuni commenti: Questo è un po 'strano, dal momento che mi aspetto che sia un Enum - Un enum è un insieme chiuso . Se Notification.Namefosse un enum, nessuno sarebbe in grado di definire nuove notifiche. Usiamo strutture per tipi simili a enum che devono consentire l'aggiunta di nuovi membri. (Vedi la proposta di evoluzione rapida .)
Rickster

2
La parte confusa è che in Swift esistono sia Notification che NSNotification : Notificationè un tipo di valore (uno struct), in modo che possa beneficiare della semantica di Swift per la mutabilità del valore (im). In genere, i tipi Foundation perdono il loro "NS" in Swift 3, ma dove esiste uno dei nuovi tipi di valore Foundation per soppiantarlo, il vecchio tipo di riferimento rimane in giro (mantenendo il nome "NS") in modo che tu possa ancora usarlo quando hai bisogno di semantica di riferimento o di sottoclassarla. Vedi la proposta .
Rickster

Vorrei chiarire: mi aspetto che i nomi delle notifiche siano enumerazioni, come lo sono gli errori. È possibile definire le proprie enumerazioni Error e renderle conformi a ErrorType.
hexdreamer

1
Vero: almeno in teoria Apple avrebbe potuto rendere NotoficationName (o qualcosa di simile) un protocollo, al quale creare tipi conformi. Non lo so, ma probabilmente c'è una ragione per cui non l'hanno fatto ... Probabilmente qualcosa a che fare con il bridging di ObjC? Segnala un bug (in open source , Foundation Swift è allo scoperto) se hai trovato una soluzione migliore.
Rickster,

2
Probabilmente hai ragione in quanto dovrebbe iniziare con lettere minuscole.
hexdreamer

13

Modo più semplice:

let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)

11

Puoi aggiungere un inizializzatore personalizzato a NSNotification.Name

extension NSNotification.Name {
    enum Notifications: String {
        case foo, bar
    }
    init(_ value: Notifications) {
        self = NSNotification.Name(value.rawValue)
    }
}

Uso:

NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)

1
'Enum type' minuscolo e 'init (_ type: type)' per Swift 3.0.2
Jalakoo

@Jalakoo Solo la cases in un enum dovrebbe essere minuscolo, non l'enum stesso. I nomi dei tipi sono in maiuscolo e le enumerazioni sono tipi.
manmal

9

Potrei suggerire un'altra opzione simile a quella suggerita da @CesarVarela.

extension Notification.Name {
    static var notificationName: Notification.Name {
        return .init("notificationName")
    }
}

Ciò ti consentirà di pubblicare e iscriverti facilmente alle notifiche.

NotificationCenter.default.post(Notification(name: .notificationName))

Spero che questo ti possa aiutare.


4

Ho realizzato la mia implementazione mescolando le cose da lì a lì e trovo che sia la più conveniente. Condivisione per chi potrebbe essere interessato:

public extension Notification {
    public class MyApp {
        public static let Something = Notification.Name("Notification.MyApp.Something")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.onSomethingChange(notification:)),
                                               name: Notification.MyApp.Something,
                                               object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @IBAction func btnTapped(_ sender: UIButton) {
        NotificationCenter.default.post(name: Notification.MyApp.Something,
                                      object: self,
                                    userInfo: [Notification.MyApp.Something:"foo"])
    }

    func onSomethingChange(notification:NSNotification) {
        print("notification received")
        let userInfo = notification.userInfo!
        let key = Notification.MyApp.Something 
        let something = userInfo[key]! as! String //Yes, this works :)
        print(something)
    }
}


2

Questo è solo un riferimento

// Add observer:
NotificationCenter.default.addObserver(self,
    selector: #selector(notificationCallback),
    name: MyClass.myNotification,
    object: nil)

    // Post notification:
    let userInfo = ["foo": 1, "bar": "baz"] as [String: Any]
    NotificationCenter.default.post(name: MyClass.myNotification,
        object: nil,
        userInfo: userInfo)

1

Il vantaggio dell'utilizzo delle enumerazioni è che facciamo in modo che il compilatore controlli che il nome sia corretto. Riduce potenziali problemi e semplifica il refactoring.

Per coloro a cui piace usare le enumerazioni invece delle stringhe tra virgolette per i nomi delle notifiche, questo codice fa il trucco:

enum MyNotification: String {
    case somethingHappened
    case somethingElseHappened
    case anotherNotification
    case oneMore
}

extension NotificationCenter {
    func add(observer: Any, selector: Selector, 
             notification: MyNotification, object: Any? = nil) {
        addObserver(observer, selector: selector, 
                    name: Notification.Name(notification.rawValue),
                    object: object)
    }
    func post(notification: MyNotification, 
              object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {
        post(name: NSNotification.Name(rawValue: notification.rawValue), 
             object: object, userInfo: userInfo)
    }
}

Quindi puoi usarlo in questo modo:

NotificationCenter.default.post(.somethingHappened)

Sebbene non sia correlato alla domanda, lo stesso può essere fatto con i seguiti dello storyboard, per evitare di digitare stringhe tra virgolette:

enum StoryboardSegue: String {
    case toHere
    case toThere
    case unwindToX
}

extension UIViewController {
    func perform(segue: StoryboardSegue) {
        performSegue(withIdentifier: segue.rawValue, sender: self)
    }
}

Quindi, sul controller della vista, chiamalo come:

perform(segue: .unwindToX)

> NotificationCenter.default.post(.somethingHappened)Questo genera un errore; i metodi che hai aggiunto nella tua estensione accettano più argomenti.

0

se utilizzi notifiche personalizzate di sola stringa, non c'è motivo di estendere le classi ma String

    extension String {
        var notificationName : Notification.Name{
            return Notification.Name.init(self)
        }
    }

0

La risposta di @ CesarVarela è buona, ma per rendere il codice leggermente più pulito, puoi fare quanto segue:

extension Notification.Name {
    typealias Name = Notification.Name

    static let onSelectedSkin = Name("on-selected-skin")
    static let onFoo = Name("on-foo")
}

0

Se vuoi che funzioni in modo pulito in un progetto che utilizza contemporaneamente Objective-C e Swift, ho trovato più facile creare le notifiche in Objective-C.

Crea un file .m / .h:

//CustomNotifications.h
#import <Foundation/Foundation.h>

// Add all notifications here
extern const NSNotificationName yourNotificationName;
//CustomNotifications.m
#import "CustomNotifications.h"

// Add their string values here
const NSNotificationName yourNotificationName = @"your_notification_as_string";

Nel tuo MyProject-Bridging-Header.h(dal nome del tuo progetto) per esporli a Swift.

#import "CustomNotifications.h"

Usa le tue notifiche in Objective-C in questo modo:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];

E in Swift (5) come questo:

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.