คุณสร้างการแจ้งเตือนแบบกำหนดเองใน Swift 3 ได้อย่างไร


112

ใน Objective-C การแจ้งเตือนที่กำหนดเองเป็นเพียง NSString ธรรมดา แต่ไม่ชัดเจนใน Swift 3 เวอร์ชัน WWDC ว่าควรจะเป็นอย่างไร


คำตอบ:


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การเปลี่ยนแปลงเท่านั้น


นี่เป็นวิธีที่ฉันคิดว่าควรใช้งานได้ในตอนแรกการแจ้งเตือนควรเป็น enums ขอบคุณสำหรับเคล็ดลับ!
hexdreamer

ไม่มีปัญหา! ฉันแก้ไขโค้ดเพื่อรวมโครงสร้างของส่วนขยายNotificationNameเพื่อเพิ่มnameคุณสมบัติให้กับ enums ที่สอดคล้องกับโปรโตคอลเท่านั้น
halil_g

IMO ที่เทียบเท่ากันอย่างเคร่งครัด แต่มีตรรกะมากกว่าคุณสามารถกำหนดส่วนขยายใน NotificationName (แทน RawRepresentable) ดังนี้extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj

388

มีวิธีที่สะอาดกว่า (ฉันคิดว่า) เพื่อให้บรรลุ

extension Notification.Name {

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

จากนั้นคุณสามารถใช้มันได้แบบนี้

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

2
ฉันใช้รหัสด้านบน นี่คือคุณสมบัติคงที่
Cesar Varela

3
สะอาดมากฉันชอบมาก
Tom Wolters

10
extension NSNotification.Nameextension Notification.Name แทน มิฉะนั้นจะร้องเรียน Swift 3 กับ'Notification' is ambiguous for type lookup in this context
lluisgh

9
คุณได้รับคะแนนโหวตของฉันสำหรับการพิมพ์ผิดในสตริงและแสดงให้เห็นถึงคุณค่าของชื่อการแจ้งเตือนที่พิมพ์: P
Dorian Roy

10
อาจเป็นที่น่าสังเกตว่านี่เป็นวิธีที่ Apple แนะนำใน WWDC 2016 Session 207 developer.apple.com/videos/play/wwdc2016/207
Leon

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 ไม่ใช่โครงสร้างแบบกำหนดเองบางอย่างที่ดูเหมือนจะไม่มีประโยชน์อีกต่อไป

มี typealias ในการแจ้งเตือนสำหรับ NSNotification.Name:

public typealias Name = NSNotification.Name

ส่วนที่ทำให้สับสนคือทั้ง Notification และ NSNotification มีอยู่ใน Swift

ดังนั้นในการกำหนดการแจ้งเตือนที่คุณกำหนดเองให้ทำบางอย่างเช่น:

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

จากนั้นจะเรียกมันว่า:

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

3
คำตอบที่ดี. ความคิดเห็นบางส่วน: นี่เป็นเรื่องแปลกเนื่องจากฉันคาดหวังว่ามันจะเป็น Enum - Enum เป็นชุดปิด ถ้าNotification.Nameเป็น enum จะไม่มีใครกำหนดการแจ้งเตือนใหม่ได้ เราใช้โครงสร้างสำหรับประเภทที่คล้ายกับ enum ที่ต้องอนุญาตให้เพิ่มสมาชิกใหม่ (ดูข้อเสนอวิวัฒนาการที่รวดเร็ว )
rickster

2
ส่วนที่ทำให้สับสนคือทั้ง Notification และ NSNotification มีอยู่ใน Swift - Notificationเป็นประเภทค่า (โครงสร้าง) เพื่อให้ได้รับประโยชน์จากความหมายของ Swift สำหรับความไม่แน่นอนของค่า (im) โดยทั่วไปประเภทของมูลนิธิจะทิ้ง "NS" ใน Swift 3 แต่ในกรณีที่ประเภทมูลค่าพื้นฐานใหม่มีอยู่เพื่อแทนที่ประเภทการอ้างอิงแบบเก่าจะเกาะติดรอบ ๆ (ยังคงชื่อ "NS" ไว้) เพื่อให้คุณยังคงสามารถใช้งานได้เมื่อ คุณต้องการความหมายอ้างอิงหรือคลาสย่อย ดูข้อเสนอ
rickster

ให้ฉันชี้แจง: ฉันคาดว่าชื่อการแจ้งเตือนจะเป็น enums เช่นข้อผิดพลาด คุณสามารถกำหนด Error enums ของคุณเองและทำให้เป็นไปตาม ErrorType
hexdreamer

1
จริง - อย่างน้อยในทางทฤษฎี Apple ก็สามารถสร้าง NotoficationName (หรือบางส่วน) เป็นโปรโตคอลที่คุณสร้างประเภทที่สอดคล้องกันได้ ฉันไม่รู้ แต่อาจมีเหตุผลที่พวกเขาไม่ได้ ... อาจจะมีอะไรเกี่ยวข้องกับ ObjC bridging? ส่งข้อบกพร่อง (ไปยังโอเพ่นซอร์ส Foundation Swift เปิดอยู่) หากคุณมีวิธีแก้ปัญหาที่ดีกว่า
rickster

2
คุณน่าจะถูกต้องเพราะควรขึ้นต้นด้วยตัวพิมพ์เล็ก
hexdreamer

13

วิธีที่ง่ายกว่า:

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

11

คุณสามารถเพิ่ม initializer แบบกำหนดเองใน 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)

1
ตัวพิมพ์เล็ก 'enum type' และ 'init (_ type: type)' สำหรับ Swift 3.0.2
Jalakoo

@ Jalakoo เฉพาะcases ใน enum เท่านั้นที่ควรจะลดลงไม่ใช่ enum เอง ชื่อประเภทเป็นตัวพิมพ์ใหญ่และ enums เป็นประเภท
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 คือเราจะได้คอมไพเลอร์ตรวจสอบว่าชื่อนั้นถูกต้อง ลดปัญหาที่อาจเกิดขึ้นและทำให้การปรับโครงสร้างใหม่ง่ายขึ้น

สำหรับผู้ที่ชอบใช้ 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)

แม้ว่าจะไม่เกี่ยวข้องกับคำถาม แต่ก็สามารถทำได้เช่นเดียวกันกับ storyboard segues เพื่อหลีกเลี่ยงการพิมพ์สตริงที่ยกมา:

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];

และใน Swift (5) เช่นนี้:

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.