En Objective-C, una notificación personalizada es simplemente un NSString simple, pero no es obvio en la versión WWDC de Swift 3 exactamente lo que debería ser.
En Objective-C, una notificación personalizada es simplemente un NSString simple, pero no es obvio en la versión WWDC de Swift 3 exactamente lo que debería ser.
Respuestas:
También puede utilizar 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 los nombres de sus notificaciones como un enum
lugar que desee. Por ejemplo:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
Y usarlo como
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
De esta manera, los nombres de las notificaciones se desvincularán de la Fundación Notification.Name
. Y solo tendrás que modificar tu protocolo en caso de que la implementación Notification.Name
cambie.
NotificationName
que la name
propiedad solo se agregue a las enumeraciones que se ajustan al protocolo.
extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
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)
extension NSNotification.Name
en lugar de extension Notification.Name
. De lo contrario, Swift 3 quejas con'Notification' is ambiguous for type lookup in this context
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 un Enum, y no una estructura personalizada sin aparentemente más beneficio.
Hay un typealias en Notification for NSNotification.Name:
public typealias Name = NSNotification.Name
La parte confusa es que tanto Notification como 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)
Notification.Name
fuera una enumeración, nadie podría definir nuevas notificaciones. Usamos estructuras para tipos similares a enumeraciones que necesitan permitir agregar nuevos miembros. (Ver la propuesta de evolución rápida .)
Notification
es un tipo de valor (una estructura), por lo que puede beneficiarse de la semántica de Swift para la (im) mutabilidad del valor. Por lo general, los tipos de base eliminan su "NS" en Swift 3, pero cuando existe uno de los nuevos tipos de valor de base para suplantarlo, el tipo de referencia anterior se mantiene (conservando el nombre "NS") para que pueda seguir utilizándolo cuando necesita semántica de referencia o subclasificarla. Vea la propuesta .
Manera más fácil:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
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)
case
s en una enumeración deben estar en minúsculas, no la enumeración en sí. Los nombres de los tipos están en mayúsculas y las enumeraciones son tipos.
Puedo sugerir otra opción similar a lo que sugirió @CesarVarela.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Esto te permitirá publicar y suscribirte a las notificaciones fácilmente.
NotificationCenter.default.post(Notification(name: .notificationName))
Espero que esto te ayudará.
Hice mi propia implementación mezclando cosas de allí y de allí, y encontré esto como lo más conveniente. Compartiendo 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)
}
}
NSNotification.Name(rawValue: "myNotificationName")
Esto es solo una 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)
La ventaja de utilizar enumeraciones es que conseguimos que el compilador compruebe que el nombre es correcto. Reduce problemas potenciales y facilita la refactorización.
Para aquellos a los que les gusta usar enumeraciones en lugar de cadenas entre comillas para los nombres de notificación, 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)
}
}
Entonces puedes usarlo así:
NotificationCenter.default.post(.somethingHappened)
Aunque no está relacionado con la pregunta, se puede hacer lo mismo con las secuencias del guión gráfico, para evitar escribir cadenas entre comillas:
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)
si usa notificaciones personalizadas solo de cadena, no hay razón para extender ninguna clase, pero String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
Si desea que esto funcione de manera limpia en un proyecto que usa Objective-C y Swift al mismo tiempo, descubrí que es más fácil crear las notificaciones en Objective-C.
Cree un archivo .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";
En su MyProject-Bridging-Header.h
(nombre de su proyecto) para exponerlos a Swift.
#import "CustomNotifications.h"
Use sus notificaciones en Objective-C de esta manera:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];
Y en Swift (5) así:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)