risposta

18

Si potrebbe 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) 
     } 
    } 
} 

E poi definire i nomi di notifica come enum ovunque si desideri . Per esempio:

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

e usarlo come

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

In questo modo i nomi di notifica saranno disaccoppiati dalla Fondazione Notification.Name. E dovrai solo modificare il tuo protocollo nel caso in cui l'implementazione per le modifiche Notification.Name.

+0

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

+0

Nessun problema! Ho modificato il codice per includere la conformazione dell'estensione a 'NotificationName' in modo che la proprietà' name' sia aggiunta alle enumerazioni conformi al protocollo. –

+0

IMO rigorosamente equivalente ma più logico, è possibile definire l'estensione su NotificationName (anziché RawRepresentable) in questo modo: 'estensione NotificationName dove Self: RawRepresentable, Self.RawValue == String {' – jlj

31

Notification.post è definito come:

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

in Objective-C, il nome di notifica è una pianura 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 aspetto che sia un Enum, e non qualche struct personalizzato con apparentemente non più beneficio.

C'è un typealias nella notifica per NSNotification.Name:

public typealias Name = NSNotification.Name 

La parte confusa è che sia di notifica e NSNotification esistono in Swift

Quindi, al fine di definire il proprio notifiche personalizzate, fare somethine come:

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

Poi chiamarlo:

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

Buona risposta. Alcuni commenti: * Questo è un po 'strano, dal momento che mi aspetterei che sia un Enum * - Un enum è un insieme * chiuso *. Se "Notification.Name" fosse un enum, nessuno sarebbe in grado di definire nuove notifiche. Usiamo le strutture per i tipi di tipo enum che devono consentire l'aggiunta di nuovi membri. (Vedi la [proposta swift-evolution] (https://github.com/apple/swift-evolution/blob/master/proposals/0033-import-objc-constants.md).) – rickster

+2

* La parte confusa è che entrambi Notifiche e NSNotification esistono in Swift * - 'Notification' è un tipo di valore (una struct), in modo che possa beneficiare della semantica di Swift per la mutabilità value (im). Generalmente, i tipi di Foundation abbandonano il loro "NS" in Swift 3, ma dove esiste uno dei nuovi Value Type Foundation per soppiantarlo, il vecchio tipo di riferimento rimane intorno (mantenendo il nome "NS") in modo che tu possa ancora usarlo quando hai bisogno di semantica di riferimento o di sottoclasse. Vedi la [proposta] (https://github.com/apple/swift-evolution/blob/master/proposals/0069-swift-mutability-for-foundation.md). – rickster

+0

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

256

C'è un detergente (credo) modo per raggiungerlo

extension Notification.Name { 

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

e poi si può usare in questo modo

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

Sto usando il codice sopra. Questa è una proprietà statica. –

+3

Questo è un modo fantastico per farlo. Votazione – crashoverride777

+3

Molto pulito, mi piace molto –

2

Questo è appena 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) 
10

È possibile aggiungere un inizializzatore personalizzato a NSNotification.Name

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

Usage:

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

Minuscolo' enum type 'e' init (_ type: type) 'per Swift 3.0.2 – Jalakoo

+0

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

10

modo più semplice:

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

Ho fatto la mia implementazione mescolando le cose da lì a lì, e trovo questo come il più conveniente. La condivisione per chi qualsiasi che 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) 
    } 
} 
1

Il vantaggio di utilizzare le enumerazioni è che arriviamo al compilatore di verificare che il nome sia corretto. Riduce i potenziali problemi e facilita il refactoring.

Per coloro che amano utilizzando le enumerazioni, invece di stringhe tra virgolette per i nomi di notifica, 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 è possibile utilizzare in questo modo:

NotificationCenter.default.post(.somethingHappened) 

Anche se estranei al domanda, lo stesso può essere fatto con storyboard segues, 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) 
    } 
} 

Poi, sul controller della vista, come lo chiamano:

perform(segue: .unwindToX) 
0

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

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