Como você cria notificações personalizadas no Swift 3?

Respostas:

32

Você também pode usar um protocolo para este

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

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

Em seguida, defina seus nomes de notificação como em enumqualquer lugar que desejar. Por exemplo:

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

E usar como

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

Desta forma, os nomes das notificações serão dissociados da Fundação Notification.Name. E você só terá que modificar seu protocolo no caso da implementação para Notification.Namealterações.

halil_g
fonte
É exatamente assim que eu pensei que deveria funcionar - as notificações deveriam ser enums. Obrigado pelo truque!
hexdreamer
Sem problemas! Eu editei o código para incluir a conformação da extensão para NotificationNameque oname propriedade só seja adicionada às enums que estão em conformidade com o protocolo.
halil_g
Estritamente equivalente, mas IMO mais lógico, você pode definir a extensão em NotificationName (em vez de RawRepresentable) assim: extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj
386

Existe uma maneira mais limpa (eu acho) de conseguir isso

extension Notification.Name {

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

E então você pode usá-lo assim

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
Cesar Varela
fonte
2
Estou usando o código acima. Esta é uma propriedade estática.
Cesar Varela
3
Muito limpo, gosto muito
Tom Wolters,
10
extension NSNotification.Name em vez de extension Notification.Name . Caso contrário, Swift 3 reclama com'Notification' is ambiguous for type lookup in this context
lluisgh
9
Você recebe meu voto positivo por cometer um erro de digitação na string e, assim, demonstrar o valor dos nomes de notificação digitados: P
Dorian Roy
10
Pode ser interessante notar que este é o método sugerido pela Apple na WWDC 2016 Session 207 developer.apple.com/videos/play/wwdc2016/207
Leon,
36

Notification.post é definido como:

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

Em Objective-C, o nome da notificação é um NSString simples. Em Swift, é definido como NSNotification.Name.

NSNotification.Name é definido como:

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

Isso é meio estranho, já que eu esperava que fosse um Enum, e não uma estrutura personalizada com aparentemente nenhum benefício.

Há um typealias em Notification for NSNotification.Name:

public typealias Name = NSNotification.Name

A parte confusa é que tanto a Notificação quanto a NSNotification existem no Swift

Portanto, para definir sua própria notificação personalizada, faça algo como:

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

Então, para chamá-lo:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)
sonhador
fonte
3
Boa resposta. Alguns comentários: Isso é meio estranho, já que eu esperava que fosse um Enum - Um enum é um conjunto fechado . Se Notification.Namefosse um enum, ninguém seria capaz de definir novas notificações. Usamos structs para tipos do tipo enum que precisam permitir a adição de novos membros. (Veja a proposta de evolução rápida .)
rickster
2
A parte confusa é que tanto Notification quanto NSNotification existem no Swift - Notificationé um tipo de valor (uma estrutura), de modo que pode se beneficiar da semântica do Swift para (im) mutabilidade de valor. Geralmente, os tipos de base estão perdendo seus "NS" no Swift 3, mas onde um dos novos Tipos de valor de base existe para substituí-lo, o tipo de referência antigo permanece (mantendo o nome "NS") para que você ainda possa usá-lo quando você precisa de semântica de referência ou subclasse. Veja a proposta .
rickster de
Deixe-me esclarecer: espero que os nomes das notificações sejam enums, como os erros. Você pode definir seus próprios enums de Error e torná-los compatíveis com ErrorType.
hexdreamer
1
Verdadeiro - a Apple poderia, pelo menos teoricamente, ter feito NotoficationName (ou algo parecido) um protocolo, para o qual você cria tipos em conformidade. Não sei, mas provavelmente há uma razão para eles não terem ... Provavelmente algo a ver com a ponte ObjC? Registre um bug (para código aberto , o Foundation Swift está em aberto) se você tiver uma solução melhor desenvolvida.
rickster de
2
Você provavelmente está correto ao dizer que deve começar com letras minúsculas.
hexdreamer
13

Maneira mais fácil:

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

Você pode adicionar um inicializador personalizado 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)
efremidze
fonte
1
Minúsculas 'enum type' e 'init (_ type: type)' para Swift 3.0.2
Jalakoo
@Jalakoo Apenas o cases em um enum deve ser minúsculo, não o próprio enum. Nomes de tipo são maiúsculos e enums são tipos.
manmal
9

Posso sugerir outra opção semelhante à sugerida por @CesarVarela.

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

Isso permitirá que você poste e se inscreva em notificações facilmente.

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

Espero que isso ajude você.

Mikhail Glotov
fonte
4

Fiz minha própria implementação misturando coisas de lá e de lá, e achei isso o mais conveniente. Compartilhando para quem possa estar interessado:

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)
    }
}
inigo333
fonte
3
NSNotification.Name(rawValue: "myNotificationName")
Lee Probert
fonte
2

Isso é apenas referência

// 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)
user6943269
fonte
1

A vantagem de usar enums é que fazemos com que o compilador verifique se o nome está correto. Reduz possíveis problemas e facilita a refatoração.

Para quem gosta de usar enums em vez de strings entre aspas para nomes de notificação, este código funciona:

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

Então você pode usá-lo assim:

NotificationCenter.default.post(.somethingHappened)

Embora não relacionado à pergunta, o mesmo pode ser feito com segues de storyboard, para evitar digitar strings entre aspas:

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

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

Então, em seu controlador de visualização, chame-o assim:

perform(segue: .unwindToX)
Eneko Alonso
fonte
> NotificationCenter.default.post(.somethingHappened)Isso gera um erro; os métodos que você adicionou em sua extensão aceitam mais argumentos.
0

se você usar notificações personalizadas apenas de string, não há razão para estender nenhuma classe, mas String

    extension String {
        var notificationName : Notification.Name{
            return Notification.Name.init(self)
        }
    }
Quang Vĩnh Hà
fonte
0

A resposta da @CesarVarela é boa, mas para deixar o código um pouco mais limpo, você pode fazer o seguinte:

extension Notification.Name {
    typealias Name = Notification.Name

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

Se você quiser que isso funcione de forma limpa em um projeto que usa Objective-C e Swift ao mesmo tempo, achei mais fácil criar as notificações em Objective-C.

Crie um arquivo .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";

Em seu MyProject-Bridging-Header.h(nome de seu projeto) para expô-los ao Swift.

#import "CustomNotifications.h"

Use suas notificações em Objective-C assim:

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

E em Swift (5) assim:

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
nickdnk
fonte