NotificationCenter issue on Swift 3 [duplicate]
Asked Answered
R

4

92

I'm learning Swift 3 and I'm trying to using NSNotificationCenter. Here is my code:

func savePost(){
    let postData = NSKeyedArchiver.archivedData(withRootObject: _loadedpost)
    UserDefaults.standard().object(forKey: KEY_POST)
}
func loadPost(){
    if let postData = UserDefaults.standard().object(forKey: KEY_POST) as? NSData{
        if let postArray = NSKeyedUnarchiver.unarchiveObject(with: postData as Data) as? [Post]{
                _loadedpost = postArray
        }
    }
    //codeerror
    NotificationCenter.default().post(NSNotification(name: "loadedPost" as NSNotification.Name, object: nil) as Notification)
}

and this is the observer:

override func viewDidLoad() {
    super.viewDidLoad()
//codeerorr
    NotificationCenter.default().addObserver(self, selector: Selector(("onPostLoaded")), name: "loadedPost", object: nil)
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

It always gives me the error "signal SIGBRT". When I try to change the name in the observer, it's not an error, but obviously it didn't show anything. How do I fix this?

Rodolphe answered 5/7, 2016 at 13:26 Comment(2)
Please post 'onPostLoaded' method implementation tooEtrem
#36911465Feverroot
D
328

Swift 3 & 4

Swift 3, and now Swift 4, have replaced many "stringly-typed" APIs with struct "wrapper types", as is the case with NotificationCenter. Notifications are now identified by a struct Notfication.Name rather than by String. For more details see the now legacy Migrating to Swift 3 guide

Swift 2.2 usage:

// Define identifier
let notificationIdentifier: String = "NotificationIdentifier"

// Register to receive notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name: notificationIdentifier, object: nil)

// Post a notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)

Swift 3 & 4 usage:

// Define identifier
let notificationName = Notification.Name("NotificationIdentifier")

// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification), name: notificationName, object: nil)

// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)

// Stop listening notification
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil)

All of the system notification types are now defined as static constants on Notification.Name; i.e. .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, etc.

You can extend Notification.Name with your own custom notifications in order to stay consistent with the system notifications:

// Definition:
extension Notification.Name {
    static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)

Swift 4.2 usage:

Same as Swift 4, except now system notifications names are part of UIApplication. So in order to stay consistent with the system notifications you can extend UIApplication with your own custom notifications instead of Notification.Name :

// Definition:
UIApplication {
    public static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}

// Usage:
NotificationCenter.default.post(name: UIApplication.yourCustomNotificationName, object: nil)
Disseisin answered 27/7, 2016 at 14:3 Comment(8)
What about parameters in the new Swift 3.0 usage? I couldn't figure out how to solve this. I always get a fatal error "unrecognized selector sent to instance..."Prakrit
@Prakrit Can you please post some example code? Thanks!Disseisin
default appears to be interpreted as strictly a keyword in Xcode 8 GM seed.Puss
@Puss Can you explain more? Does this code no longer work?Disseisin
@Jeffrey Fulton, it appears I spoke too soon. Working through other Swift 3 code changes finally appeased the compiler with regard to NotificationCenter.default. Sorry for the false alarm.Puss
@Puss No problem! I'm just glad we're seeing the same behaviour. I got scared for a minute.Disseisin
@JeffreyFulton I get a Compiler Segmentation Fault: 11 when doing extension Notification.Name { ... } however changing to NSNotification.Name resolves the issue: extension NSNotification.Name { ... }Lossa
Note: the userInfo now takes [AnyHashable:Any]? as an argument, which we provide as a dictionary literal in Swift. #36911465Feverroot
F
18

Notifications appear to have changed again (October 2016).

// Register to receive notification

NotificationCenter.default.addObserver(self, selector: #selector(yourClass.yourMethod), name: NSNotification.Name(rawValue: "yourNotificatioName"), object: nil)

// Post notification

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "yourNotificationName"), object: nil)
Fourthly answered 9/10, 2016 at 17:29 Comment(1)
Did you try using Notification in place of NSNotification as per my answer above? The two classes are "bridged" meaning they can be used interchangeably. More info in the "Swift Foundation Overlay" section at developer.apple.com/reference/foundation/…Disseisin
O
18

For all struggling around with the #selector in Swift 3 or Swift 4, here a full code example:

// WE NEED A CLASS THAT SHOULD RECEIVE NOTIFICATIONS
    class MyReceivingClass {

    // ---------------------------------------------
    // INIT -> GOOD PLACE FOR REGISTERING
    // ---------------------------------------------
    init() {
        // WE REGISTER FOR SYSTEM NOTIFICATION (APP WILL RESIGN ACTIVE)

        // Register without parameter
        NotificationCenter.default.addObserver(self, selector: #selector(MyReceivingClass.handleNotification), name: .UIApplicationWillResignActive, object: nil)

        // Register WITH parameter
        NotificationCenter.default.addObserver(self, selector: #selector(MyReceivingClass.handle(withNotification:)), name: .UIApplicationWillResignActive, object: nil)
    }

    // ---------------------------------------------
    // DE-INIT -> LAST OPTION FOR RE-REGISTERING
    // ---------------------------------------------
    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    // either "MyReceivingClass" must be a subclass of NSObject OR selector-methods MUST BE signed with '@objc'

    // ---------------------------------------------
    // HANDLE NOTIFICATION WITHOUT PARAMETER
    // ---------------------------------------------
    @objc func handleNotification() {
        print("RECEIVED ANY NOTIFICATION")
    }

    // ---------------------------------------------
    // HANDLE NOTIFICATION WITH PARAMETER
    // ---------------------------------------------
    @objc func handle(withNotification notification : NSNotification) {
        print("RECEIVED SPECIFIC NOTIFICATION: \(notification)")
    }
}

In this example we try to get POSTs from AppDelegate (so in AppDelegate implement this):

// ---------------------------------------------
// WHEN APP IS GOING TO BE INACTIVE
// ---------------------------------------------
func applicationWillResignActive(_ application: UIApplication) {

    print("POSTING")

    // Define identifiyer
    let notificationName = Notification.Name.UIApplicationWillResignActive

    // Post notification
    NotificationCenter.default.post(name: notificationName, object: nil)
}
Oratorio answered 22/10, 2016 at 19:12 Comment(4)
nice, thorough answer. Quick question. I know in objective-C we need to be paranoid about removing the observer when leaving the view (or bad stuff happens). Is this still the case in Swift3? I'm asking because I don't see many other answers mentioning this, while you explicitly make this clear.Sext
I don't know, wether it is as important as it was in objective-C, but generally I think it is good practice, to care for unregistering, when you don't need to be notified further more. You register, when you need be notified and you re-register, when you don't need it any more.Oratorio
I can now confirm dismissing is still important. :) It seems kind of obvious in retrospect, but if you don't dismiss the observer, you run the risk of having MULTIPLE observers firing. So remember to dismiss in Swift, or bad stuff happens!Sext
This was the perfect answer to help me understand!Staub
C
5

I think it has changed again.

For posting this works in Xcode 8.2.

NotificationCenter.default.post(Notification(name:.UIApplicationWillResignActive)
Chichi answered 6/1, 2017 at 0:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.