Bagaimana Anda membuat pemberitahuan khusus di Swift 3?

Jawaban:

32

Anda juga bisa menggunakan protokol untuk ini

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

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

Dan kemudian tentukan nama notifikasi Anda sebagai enummana pun yang Anda inginkan. Sebagai contoh:

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

Dan gunakan seperti itu

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

Dengan cara ini nama notifikasi akan dipisahkan dari Foundation Notification.Name. Dan Anda hanya perlu memodifikasi protokol Anda jika ada Notification.Nameperubahan implementasi .

halil_g
sumber
Ini persis seperti yang saya pikir semula seharusnya berfungsi - notifikasi harus enum. Terima kasih atas triknya!
hexdreamer
Tidak masalah! Saya mengedit kode untuk memasukkan konformasi ekstensi NotificationNamesehingga nameproperti hanya ditambahkan ke enum yang sesuai dengan protokol.
halil_g
Benar-benar setara tetapi lebih logis, Anda dapat menentukan ekstensi di NotificationName (bukan RawRepresentable) seperti ini:extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj
387

Ada cara yang lebih bersih (menurut saya) untuk mencapainya

extension Notification.Name {

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

Dan kemudian Anda bisa menggunakannya seperti ini

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
Cesar Varela
sumber
2
Saya menggunakan kode di atas. Ini adalah properti statis.
Cesar Varela
3
Sangat bersih, saya sangat menyukainya
Tom Wolters
10
extension NSNotification.Name bukannya extension Notification.Name . Jika tidak, Swift 3 mengeluh dengan'Notification' is ambiguous for type lookup in this context
lluisgh
9
Anda mendapatkan upvote saya karena membuat kesalahan ketik dalam string dan dengan demikian menunjukkan nilai nama pemberitahuan yang diketik: P
Dorian Roy
10
Perlu dicatat bahwa ini adalah metode yang disarankan oleh Apple di WWDC 2016 Sesi 207 developer.apple.com/videos/play/wwdc2016/207
Leon
36

Notification.post didefinisikan sebagai:

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

Di Objective-C, nama notifikasi adalah NSString biasa. Di Swift, ini didefinisikan sebagai NSNotification.Name.

NSNotification.Name didefinisikan sebagai:

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

Ini agak aneh, karena saya berharap itu menjadi Enum, dan bukan beberapa struct kustom dengan manfaat yang tampaknya tidak lebih.

Ada typealias di Notification for NSNotification.Name:

public typealias Name = NSNotification.Name

Bagian yang membingungkan adalah Notification dan NSNotification ada di Swift

Jadi untuk menentukan notifikasi kustom Anda sendiri, lakukan sesuatu seperti:

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

Kemudian menyebutnya:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)
hexdreamer
sumber
3
Jawaban yang bagus. Beberapa komentar: Ini agak aneh, karena saya berharap itu menjadi Enum - Enum adalah set tertutup . Jika Notification.Namemerupakan enum, tidak ada yang dapat menentukan notifikasi baru. Kami menggunakan struct untuk tipe sebaliknya-enum yang perlu mengizinkan penambahan anggota baru. (Lihat proposal evolusi-cepat .)
rickster
2
Bagian yang membingungkan adalah bahwa Notification dan NSNotification ada di Swift - Notificationmerupakan tipe nilai (struct), sehingga dapat memanfaatkan semantik Swift untuk nilai (im) mutabilitas. Umumnya, jenis Fondasi menghilangkan "NS" mereka di Swift 3, tetapi di mana salah satu Jenis Nilai Fondasi baru ada untuk menggantikannya, jenis referensi lama tetap ada (tetap menggunakan nama "NS") sehingga Anda masih dapat menggunakannya saat Anda memerlukan semantik referensi atau untuk membuat subkelasnya. Lihat proposalnya .
rickster
Izinkan saya mengklarifikasi: Saya berharap nama notifikasi menjadi enum, seperti Error. Anda dapat menentukan enum Error Anda sendiri, dan membuatnya sesuai dengan ErrorType.
hexdreamer
1
Benar - Apple setidaknya secara teoritis dapat membuat NotoficationName (atau semacamnya) sebuah protokol, yang Anda buat jenisnya sesuai. Entahlah, tapi mungkin ada alasan mengapa mereka tidak ... Mungkin ada hubungannya dengan ObjC bridging? Laporkan bug (ke open source , Foundation Swift terbuka) jika Anda memiliki solusi yang lebih baik.
rickster
2
Anda mungkin benar karena harus dimulai dengan huruf kecil.
hexdreamer
13

Cara yang lebih mudah:

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

Anda dapat menambahkan penginisialisasi khusus ke NSNotification.Name

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

Pemakaian:

NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)
efremidze
sumber
1
Huruf kecil 'enum type' dan 'init (_ type: type)' untuk Swift 3.0.2
Jalakoo
@Jalakoo Hanya cases dalam enum yang harus dikecilkan , bukan enum itu sendiri. Nama tipe diberi huruf besar, dan enum adalah tipe.
manmal
9

Saya mungkin menyarankan opsi lain yang mirip dengan apa yang disarankan @CesarVarela.

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

Ini akan memungkinkan Anda memposting dan berlangganan pemberitahuan dengan mudah.

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

Semoga ini bisa membantu Anda.

Mikhail Glotov
sumber
4

Saya melakukan implementasi saya sendiri dengan mencampur berbagai hal dari sana dan di sana, dan menemukan ini sebagai yang paling nyaman. Berbagi untuk siapa saja yang mungkin tertarik:

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

Ini hanya referensi

// 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)
pengguna6943269
sumber
1

Keuntungan menggunakan enum adalah kita meminta kompiler untuk memeriksa apakah namanya benar. Mengurangi potensi masalah dan mempermudah pemfaktoran ulang.

Bagi mereka yang suka menggunakan enum daripada kutipan string untuk nama notifikasi, kode ini melakukan triknya:

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

Kemudian Anda bisa menggunakannya seperti ini:

NotificationCenter.default.post(.somethingHappened)

Meskipun tidak terkait dengan pertanyaan, hal yang sama dapat dilakukan dengan segmen storyboard, untuk menghindari mengetik string yang dikutip:

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

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

Kemudian, pada pengontrol tampilan Anda, sebut seperti:

perform(segue: .unwindToX)
Eneko Alonso
sumber
> NotificationCenter.default.post(.somethingHappened)Ini melempar kesalahan; metode yang Anda tambahkan di ekstensi menerima lebih banyak argumen.
0

jika Anda menggunakan notifikasi khusus khusus string, tidak ada alasan untuk memperluas kelas apa pun kecuali String

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

Jawaban @ CesarVarela bagus, tetapi untuk membuat kodenya sedikit lebih bersih, Anda dapat melakukan hal berikut:

extension Notification.Name {
    typealias Name = Notification.Name

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

Jika Anda ingin ini bekerja dengan bersih dalam proyek yang menggunakan Objective-C dan Swift pada saat yang sama, menurut saya akan lebih mudah untuk membuat notifikasi di Objective-C.

Buat file .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";

Di Anda MyProject-Bridging-Header.h(dinamai menurut proyek Anda) untuk mengekspos mereka ke Swift.

#import "CustomNotifications.h"

Gunakan notifikasi Anda di Objective-C seperti ini:

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

Dan di Swift (5) seperti ini:

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