web-dev-qa-db-fra.com

Comment créez-vous des notifications personnalisées dans Swift 3?

En Objective-C, une notification personnalisée n’est qu’une simple NSString, mais la version WWDC de Swift 3 n’est pas évidente.

93
hexdreamer

Vous pouvez aussi utiliser un protocole pour cela

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

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

Et ensuite, définissez vos noms de notification en tant que enum où vous voulez. Par exemple:

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

Et l'utiliser comme

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

De cette façon, les noms des notifications seront découplés du Foundation Notification.Name. Et vous ne devrez modifier votre protocole qu'au cas où la mise en œuvre de Notification.Name changerait.

27
halil_g

Il y a un moyen plus propre (je pense) d'y parvenir

extension Notification.Name {

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

Et puis vous pouvez l'utiliser comme ça

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

Notification.post est défini comme:

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

En Objective-C, le nom de la notification est un NSString en clair. Dans Swift, il est défini comme NSNotification.Name.

NSNotification.Name est défini comme:

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

C'est un peu étrange, car je m'attendrais à ce que ce soit un Enum, et non une structure personnalisée avec apparemment plus d'avantages.

Il y a un typealias dans Notification pour NSNotification.Name:

public typealias Name = NSNotification.Name

La partie déroutante est qu’il existe à la fois Notification et NSNotification dans Swift.

Donc, afin de définir votre propre notification personnalisée, procédez comme suit:

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

Puis l'appeler:

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

Manière plus facile:

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

Vous pouvez ajouter un initialiseur personnalisé à 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)
10
efremidze

J'ai fait ma propre mise en œuvre en mélangeant des choses de part et d'autre, et je trouve cela le plus pratique. Partage pour qui cela pourrait intéresser:

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

Je peux suggérer une autre option semblable à celle suggérée par @CesarVarela.

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

Cela vous permettra de poster et de vous abonner facilement aux notifications.

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

J'espère que ceci vous aidera.

3
Mikhail Glotov
NSNotification.Name(rawValue: "myNotificationName")
3
Lee Probert

C'est juste référence

// 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)
2
user6943269

L'avantage d'utiliser enums est que nous demandons au compilateur de vérifier que le nom est correct. Réduit les problèmes potentiels et facilite la refactorisation.

Pour ceux qui aiment utiliser des énumérations au lieu de chaînes entre guillemets pour les noms de notification, ce code fait l'affaire:

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

Ensuite, vous pouvez l'utiliser comme ceci:

NotificationCenter.default.post(.somethingHappened)

Bien que cela ne soit pas lié à la question, la même chose peut être faite avec les légendes du storyboard, pour éviter de taper des chaînes entre guillemets:

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

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

Ensuite, sur votre contrôleur de vue, appelez-le comme suit:

perform(segue: .unwindToX)
1
Eneko Alonso

si vous utilisez des notifications personnalisées composées uniquement de chaînes, il n'y a aucune raison d'étendre des classes mais String

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

La réponse de @ CesarVarela est bonne, mais pour rendre le code légèrement plus propre, vous pouvez procéder comme suit:

extension Notification.Name {
    typealias Name = Notification.Name

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