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?

Wyetro
  • 7,986
  • 9
  • 42
  • 61
RoccoBerry
  • 951
  • 1
  • 7
  • 10
  • Please post 'onPostLoaded' method implementation too – lubilis Jul 05 '16 at 14:41
  • http://stackoverflow.com/questions/36910965/how-can-i-pass-received-data-through-nsnotificationcenter-in-my-swift-ios-app/36911168#36911168 – Sahil Oct 26 '16 at 08:05

4 Answers4

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)
Climbatize
  • 1,025
  • 17
  • 29
Jeffrey Fulton
  • 4,120
  • 1
  • 15
  • 16
  • 1
    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..." – rmnblm Aug 25 '16 at 21:19
  • @rmnblm Can you please post some example code? Thanks! – Jeffrey Fulton Aug 26 '16 at 22:41
  • default appears to be interpreted as strictly a keyword in Xcode 8 GM seed. – Bobjt Sep 09 '16 at 16:48
  • @Bobjt Can you explain more? Does this code no longer work? – Jeffrey Fulton Sep 09 '16 at 16:56
  • @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. – Bobjt Sep 09 '16 at 17:01
  • @Bobjt No problem! I'm just glad we're seeing the same behaviour. I got scared for a minute. – Jeffrey Fulton Sep 09 '16 at 17:04
  • @JeffreyFulton I get a Compiler Segmentation Fault: 11 when doing extension Notification.Name { ... } however changing to NSNotification.Name resolves the issue: extension NSNotification.Name { ... } – Daddycat Tan Yin See Oct 20 '16 at 08:56
  • Note: the userInfo now takes [AnyHashable:Any]? as an argument, which we provide as a dictionary literal in Swift. http://stackoverflow.com/questions/36910965/how-can-i-pass-received-data-through-nsnotificationcenter-in-my-swift-ios-app/36911168#36911168 – Sahil Oct 26 '16 at 08:08
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)
ICL1901
  • 7,211
  • 13
  • 86
  • 130
  • 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 https://developer.apple.com/reference/foundation/nsnotification#overview – Jeffrey Fulton Oct 15 '16 at 15:53
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)
}
Eric Aya
  • 68,765
  • 33
  • 165
  • 232
LukeSideWalker
  • 5,686
  • 2
  • 25
  • 38
  • 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. – timothykc Nov 30 '16 at 18:29
  • 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. – LukeSideWalker Dec 01 '16 at 08:48
  • 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! – timothykc Dec 03 '16 at 11:47
  • This was the perfect answer to help me understand! – Aᴄʜᴇʀᴏɴғᴀɪʟ Jan 04 '17 at 12:31
5

I think it has changed again.

For posting this works in Xcode 8.2.

NotificationCenter.default.post(Notification(name:.UIApplicationWillResignActive)
Pang
  • 8,605
  • 144
  • 77
  • 113
Muge Cevik
  • 176
  • 2
  • 6