Як створювати власні сповіщення у Swift 3?


Відповіді:


32

Ви також можете використовувати для цього протокол

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

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

А потім визначте свої імена сповіщень як enumде завгодно. Наприклад:

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

І використовувати це як

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

Таким чином імена сповіщень будуть зняті з фонду Notification.Name. І вам доведеться змінювати свій протокол лише у випадку, якщо впровадження Notification.Nameзмін.


Це саме те, як я спочатку вважав, що це має працювати - сповіщення мають бути перерахунками. Дякую за трюк!
hexdreamer

Нема проблем! Я редагував код включити конформації розширення для NotificationNameтому nameвластивість додається тільки до перечислениям , які відповідають протоколу.
halil_g

Суворіше еквівалентний, але більш логічний IMO, ви можете визначити розширення на NotificationName (замість RawRepresentable) так:extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj

386

Існує більш чистий (я думаю) спосіб досягти цього

extension Notification.Name {

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

І тоді ви можете використовувати це так

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

2
Я використовую код вище. Це статична властивість.
Сезар Варела

3
Дуже чисто, мені це дуже подобається
Том Волтерс

10
extension NSNotification.Name замість extension Notification.Name . В іншому випадку Swift 3 скарги на'Notification' is ambiguous for type lookup in this context
lluisgh

9
Ви отримуєте мою заяву за введення помилки в рядку і тим самим демонструєте значення набраних імен сповіщень: P
Доріан Рой

10
Можливо, варто відзначити, що це метод, запропонований Apple в WWDC 2016 Session 207 developer.apple.com/videos/play/wwdc2016/207
Леон,

36

Notification.post визначається як:

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

У Objective-C назва сповіщення - це звичайна NSString. У Swift це визначається як NSNotification.Name.

NSNotification.Name визначається як:

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

Це щось дивно, оскільки я б очікував, що це буде Enum, а не якась спеціальна структура, яка, здавалося б, не має більше користі.

Існує типізація в повідомленні для NSNotification.Name:

public typealias Name = NSNotification.Name

Заплутаною частиною є те, що як повідомлення, так і NSNotification існують у Swift

Отже, щоб визначити власне власне сповіщення, зробіть щось таке:

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

Тоді щоб зателефонувати:

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

3
Хороша відповідь. Деякі коментарі: Це щось дивно, оскільки я б очікував, що це буде Enum - Enum - це закритий набір. Якби Notification.Nameперерахунок, ніхто не зміг би визначити нові сповіщення. Ми використовуємо структури для інакше перелічених типів, яким потрібно додати нових членів. (Див . Пропозицію про швидку еволюцію .)
рикстер

2
Заплутаною частиною є те, що і Notification, і NSNotification існують у Swift - Notificationце тип значення (структура), так що він може отримати вигоду з семантики Swift для значення (im) змінності. Як правило, типи фонду скидають свої "NS" у Swift 3, але там, де існує один із нових типів Value Foundation, щоб замінити його, старий тип опорного типу залишається навколо (зберігаючи назву "NS"), щоб ви все ще могли використовувати його, коли вам потрібна довідкова семантика або її підклас. Дивіться пропозицію .
рикстер

Дозвольте уточнити: я очікую, що імена сповіщень будуть перерахунками, як помилки. Ви можете визначити власні перерахунки помилок та привести їх у відповідність до ErrorType.
hexdreamer

1
Щоправда - Apple могла хоча б теоретично скласти NotoficationName (або якийсь такий) протокол, до якого ви створюєте відповідні типи. Я не знаю, але є певна причина, що вони цього не зробили ... Напевно, щось пов'язане з мостом ObjC? Подайте помилку (щоб відкрити джерело , Foundation Swift відкритий), якщо у вас є краще рішення.
рикстер

2
Ви, мабуть, правильні, що це має починатися з малих літер.
hexdreamer

13

Найпростіший спосіб:

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

11

Ви можете додати спеціальний ініціалізатор до NSNotification.Name

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

Використання:

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


@Jalakoo Тільки cases у enum має бути нижчим регістром, а не сам enum. Назви типів мають великі літери, а перерахунки - типи.
manmal

9

Я можу запропонувати інший варіант, подібний до запропонованого @CesarVarela.

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

Це дозволить вам легко публікувати та підписуватись на сповіщення.

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

Сподіваюся, що це вам допоможе.


4

Я зробив власну реалізацію, змішуючи речі звідти і там, і вважаю це найбільш зручним. Спільний доступ для когось, хто може зацікавити:

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


2

Це просто довідка

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

1

Перевага використання enums полягає в тому, що ми отримуємо компілятор, щоб перевірити правильність імені. Зменшує потенційні проблеми та спрощує рефакторинг.

Для тих, хто любить використовувати переписки замість цитованих рядків для імен сповіщень, цей код робить фокус:

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

Тоді ви можете використовувати його так:

NotificationCenter.default.post(.somethingHappened)

Хоча це не пов’язано з питанням, те ж саме можна зробити і з розповідями, що укладають розповіді, щоб уникнути набору рядків, що цитуються:

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

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

Потім на контролері перегляду назвіть це так:

perform(segue: .unwindToX)

> NotificationCenter.default.post(.somethingHappened)Це призводить до помилки; методи, які ви додали у своє розширення, приймають більше аргументів.

0

якщо ви використовуєте користувацькі сповіщення лише для рядків, немає ніяких причин продовжувати будь-які класи, але String

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

0

@ CesarVarela відповідь хороша, але щоб зробити код трохи більш чистим, ви можете зробити наступне:

extension Notification.Name {
    typealias Name = Notification.Name

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

0

Якщо ви хочете, щоб це чітко працювало в проекті, який одночасно використовує і Objective-C, і Swift, мені було легше створити сповіщення в Objective-C.

Створіть .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";

У своєму MyProject-Bridging-Header.h(названому на честь вашого проекту) виставити їх на Swift.

#import "CustomNotifications.h"

Використовуйте сповіщення в Objective-C так:

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

А у Свіфта (5) так:

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.