defaultcenter - swift nsnotificationcenter example
¿Cómo se crean notificaciones personalizadas en Swift 3? (12)
En Objective-C, una notificación personalizada es solo un NSString simple, pero no es obvio en la versión WWDC de Swift 3 exactamente lo que debería ser.
Esto es solo referencia
// 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)
Hay una forma más limpia (creo) de lograrlo
extension Notification.Name {
static let onSelectedSkin = Notification.Name("on-selected-skin")
}
Y luego puedes usarlo así
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
Hice mi propia implementación mezclando cosas de un lado a otro, y considero que esto es lo más conveniente. Compartir para quien pueda estar interesado:
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)
}
}
La respuesta de @ CesarVarela es buena, pero para que el código sea un poco más limpio, puede hacer lo siguiente:
extension Notification.Name {
typealias Name = Notification.Name
static let onSelectedSkin = Name("on-selected-skin")
static let onFoo = Name("on-foo")
}
La ventaja de usar enumeraciones es que obtenemos el compilador para verificar que el nombre sea correcto. Reduce posibles problemas y facilita la refactorización.
Para aquellos a quienes les gusta usar enumeraciones en lugar de cadenas citadas para nombres de notificaciones, este código hace el truco:
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)
}
}
Entonces puedes usarlo así:
NotificationCenter.default.post(.somethingHappened)
Aunque no está relacionado con la pregunta, lo mismo se puede hacer con segues del guión gráfico, para evitar escribir cadenas citadas:
enum StoryboardSegue: String {
case toHere
case toThere
case unwindToX
}
extension UIViewController {
func perform(segue: StoryboardSegue) {
performSegue(withIdentifier: segue.rawValue, sender: self)
}
}
Luego, en su controlador de vista, llámelo como:
perform(segue: .unwindToX)
Manera más fácil:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
Notification.post se define como:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
En Objective-C, el nombre de la notificación es un NSString simple. En Swift, se define como NSNotification.Name.
NSNotification.Name se define como:
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
Esto es un poco extraño, ya que esperaría que fuera una Enum, y no una estructura personalizada con aparentemente no más beneficio.
Hay un typealias en Notification for NSNotification.Name:
public typealias Name = NSNotification.Name
La parte confusa es que tanto la Notificación como la NSNotification existen en Swift
Entonces, para definir su propia notificación personalizada, haga algo como:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Entonces para llamarlo:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Puede agregar un 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)
Puedo sugerir otra opción que es similar a lo que sugirió @CesarVarela.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Esto te permitirá publicar y suscribirte en notificaciones fácilmente.
NotificationCenter.default.post(Notification(name: .notificationName))
Espero que esto te ayudará.
También podría usar un protocolo para esto
protocol NotificationName {
var name: Notification.Name { get }
}
extension RawRepresentable where RawValue == String, Self: NotificationName {
var name: Notification.Name {
get {
return Notification.Name(self.rawValue)
}
}
}
Y luego defina sus nombres de notificación como una
enum
cualquier lugar que desee.
Por ejemplo:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
Y úsalo como
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
De esta forma, los nombres de las notificaciones se desacoplarán de Foundation
Notification.Name
.
Y solo tendrá que modificar su protocolo en caso de que cambie la implementación de
Notification.Name
.
si usa notificaciones personalizadas solo de cadena, no hay razón para extender ninguna clase excepto
String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
NSNotification.Name(rawValue: "myNotificationName")