Berry Blue
Berry Blue

Reputation: 16522

NSNotificationCenter addObserver in Swift

How do you add an observer in Swift to the default notification center? I'm trying to port this line of code that sends a notification when the battery level changes.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];

Upvotes: 468

Views: 437128

Answers (17)

Brianna Doubt
Brianna Doubt

Reputation: 709

My favorite way of accomplishing the same thing is with Combine's .sink. This avoids needing to mark your handler with @objc.

First, create a Notification however you want. I like to use a couple extensions

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

extension Notification {
    static func tap(_ point: CGPoint) -> Notification {
        Notification(name: .tap, object: point)
    }
}

UIKit / Custom Object

Then, create a register func in an object that calls NotificationCenter.default.publisher(for:):

import Combine

class TapNotifier {
    var cancellables: Set<AnyCancellable> = []

    func register() {
        NotificationCenter.default
            .publisher(for: .tap)
            .sink { [weak self] tapNotification in
                // This block is invoked any time a `.tap` notification is received.

                // Unwrap the sent object
                guard let point = tapNotification.object as? CGPoint else {
                    return
                }

                // Call your handler function
                self?.tapped(at: point)
            }
            .store(in: &cancellables)
    }

    func tapped(at point: CGPoint) {
        print("Tapped at \(point)")
    }
}

SwiftUI iOS 15<

The NotificationCenter.default publisher can also be observed on a SwiftUI view like this:

extension Notification {
    static var tap: Notification {
        Notification(name: .tap)
    }
}

struct TapsView: View {
    @State var taps: Int = 0

    var message: String {
        "Tapped button \(taps) " + (taps == 1 ? "time" : "times")
    }

    var body: some View {
        Text(message)
            .onReceive(NotificationCenter.default.publisher(for: .tap)) { tapNotification in
                // This block is invoked any time a `.tap` notification is received.
                taps += 1
            }
            .onTapGesture {
                NotificationCenter.default.post(.tap))
            }
    }
}

SwiftUI iOS 16+

struct TapsView: View {
    @State var taps: Int = 0

    var message: String {
        "Tapped button \(taps) " + (taps == 1 ? "time" : "times")
    }

    var body: some View {
        Text(message)
            .onReceive(NotificationCenter.default.publisher(for: .tap)) { tapNotification in
                // This block is invoked any time a `.tap` notification is received.

                // Unwrap the sent object
                guard let point = tapNotification.object as? CGPoint else {
                    return
                }

                taps += 1
            }
            .onTapGesture { point in
                NotificationCenter.default.post(.tap(point))
            }
    }
}

Upvotes: 2

Sasinderan N
Sasinderan N

Reputation: 87

  1. Create an objc function which you want to execute when notification is called.
 @objc func reloadNotification(_ notification: Notification) {
        tblview.reloadData()
    }
  1. Add the notification observer in view did load.
  NotificationCenter.default.addObserver(self, selector:#selector(reloadNotification(_:)), name: Notification.Name("reloadSideMenuDataNS"),object: nil) 
  1. post your notification where you want to call the function.
 NotificationCenter.default.post(name: Notification.Name("reloadSideMenuDataNS"), object: nil) 
  1. you can remove your notification in view did disappear with below.
 NotificationCenter.default.removeObserver(self, name: Notification.Name("reloadSideMenuDataNS"), object: nil)

Upvotes: 5

Bera Bhavin
Bera Bhavin

Reputation: 703

This is very simple example of custom notification observer and post

Add Notification Observer

NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: Notification.Name("CustomeNotificationName"), object: nil)

Add Selector and handle Observer call

@objc func myFunction(notification: Notification) {
    
    //Write you code
}

Post Notification(Observer) when it is required.

NotificationCenter.default.post(name: NSNotification.Name("CustomeNotificationName"), object: "Object", userInfo: ["key":"Value"])

Notes:- Make user when you leave screen you need to remove observer. e.g.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self);
}

Upvotes: 4

Imran Rasheed
Imran Rasheed

Reputation: 956

Swift 5 Notification Observer

override func viewDidLoad() {
    super.viewDidLoad() 
    NotificationCenter.default.addObserver(self, selector: #selector(batteryLevelChanged), name: UIDevice.batteryLevelDidChangeNotification, object: nil)
}

@objc func batteryLevelChanged(notification : NSNotification){
    //do here code
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: UIDevice.batteryLevelDidChangeNotification, object: nil)

}

Upvotes: 5

Warif Akhand Rishi
Warif Akhand Rishi

Reputation: 24248

  1. Declare a notification name

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
    
  2. You can add observer in two ways:

    Using Selector

    NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: .purchaseDidFinish, object: nil)
    
    @objc func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }
    

    or using block

    NotificationCenter.default.addObserver(forName: .purchaseDidFinish, object: nil, queue: nil) { [weak self] (notification) in
        guard let strongSelf = self else {
            return
        }
    
        strongSelf.myFunction(notification: notification)
    }
    
    func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }
    
  3. Post your notification

    NotificationCenter.default.post(name: .purchaseDidFinish, object: "myObject", userInfo: ["key": "Value"])
    

from iOS 9 and OS X 10.11. It is no longer necessary for an NSNotificationCenter observer to un-register itself when being deallocated. more info

For a block based implementation you need to do a weak-strong dance if you want to use self inside the block. more info

Block based observers need to be removed more info

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)

Upvotes: 53

Connor
Connor

Reputation: 64674

It's the same as the Objective-C API, but uses Swift's syntax.

Swift 4.2 & Swift 5:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(self.batteryLevelChanged),
    name: UIDevice.batteryLevelDidChangeNotification,
    object: nil)

If your observer does not inherit from an Objective-C object, you must prefix your method with @objc in order to use it as a selector.

@objc private func batteryLevelChanged(notification: NSNotification){     
    //do stuff using the userInfo property of the notification object
}

See NSNotificationCenter Class Reference, Interacting with Objective-C APIs

Upvotes: 475

swiftBoy
swiftBoy

Reputation: 35783

In Swift 5

Let's say if want to Receive Data from ViewControllerB to ViewControllerA

ViewControllerA (Receiver)

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Code for Passing Data through Notification Observer - - - - -
        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: - - - - - Method for receiving Data through Post Notificaiton - - - - -
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllerB (Sender)

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Set data for Passing Data Post Notification - - - - -
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }

}

Upvotes: 11

David.Chu.ca
David.Chu.ca

Reputation: 38684

Swift 5 & Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)

Upvotes: 0

Renish Dadhaniya
Renish Dadhaniya

Reputation: 10752

Swift 4.0 & Xcode 9.0+:

Send(Post) Notification:

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

OR

NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil, userInfo: ["Renish":"Dadhaniya"])

Receive(Get) Notification:

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Function-Method handler for received Notification:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

Send(Post) Notification:

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

Receive(Get) Notification:

NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Method handler for received Notification:

func methodOfReceivedNotification(notification: Notification) {
  // Take Action on Notification
}

Remove Notification:

deinit {
  NotificationCenter.default.removeObserver(self, name: Notification.Name("NotificationIdentifier"), object: nil)
}

Swift 2.3 & Xcode 7:

Send(Post) Notification

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Receive(Get) Notification

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:"NotificationIdentifier", object: nil)

Method handler for received Notification

func methodOfReceivedNotification(notification: NSNotification){
  // Take Action on Notification
}


For historic Xcode versions...



Send(Post) Notification

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Receive(Get) Notification

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOfReceivedNotification:", name:"NotificationIdentifier", object: nil)

Remove Notification

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Remove from all notifications being observed

Method handler for received Notification

func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

Annotate either the class or the target method with @objc

@objc private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

Upvotes: 845

Ashim Dahal
Ashim Dahal

Reputation: 1147

NSNotificationCenter add observer syntax in Swift 4.0 for iOS 11

  NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

This is for keyboardWillShow notification name type. Other type can be selected from the available option

the Selector is of type @objc func which handle how the keyboard will show ( this is your user function )

Upvotes: 1

Dhruv
Dhruv

Reputation: 72

In swift 3, Xcode 8.2:- checking battery state level

//Add observer
NotificationCenter.default.addObserver(self, selector: #selector(batteryStateDidChange), name: NSNotification.Name.UIDeviceBatteryStateDidChange, object: nil)


 //Fired when battery level changes

 func batteryStateDidChange(notification: NSNotification){
        //perform manipulation here
    }

Upvotes: 1

Pankaj Jangid
Pankaj Jangid

Reputation: 832

We should remove notification also.

Ex.

deinit 
{
  NotificationCenter.default.removeObserver(self, name:NSNotification.Name(rawValue: "notify"), object: nil)

}

Upvotes: 2

Sahil
Sahil

Reputation: 9226

Pass Data using NSNotificationCenter

You can also pass data using NotificationCentre in swift 3.0 and NSNotificationCenter in swift 2.0.

Swift 2.0 Version

Pass info using userInfo which is a optional Dictionary of type [NSObject : AnyObject]?

let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
 NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: imageDataDict)

// Register to receive notification in your class
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: notificationName, object: nil)

// handle notification
func showSpinningWheel(notification: NSNotification) {
  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Swift 3.0 Version

The userInfo now takes [AnyHashable:Any]? as an argument, which we provide as a dictionary literal in Swift

let imageDataDict:[String: UIImage] = ["image": image]

// post a notification
 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict) 
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// handle notification
func showSpinningWheel(_ notification: NSNotification) {

  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Source pass data using NotificationCentre(swift 3.0) and NSNotificationCenter(swift 2.0)

Upvotes: 8

Jeffrey Fulton
Jeffrey Fulton

Reputation: 4146

Swift 3.0 in Xcode 8

Swift 3.0 has 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. See the Migrating to Swift 3 guide.

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

New Swift 3.0 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)

All of the system notification types are now defined as static constants on Notification.Name; i.e. .UIDeviceBatteryLevelDidChange, .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)

Upvotes: 41

Jon Colverson
Jon Colverson

Reputation: 3066

A nice way of doing this is to use the addObserver(forName:object:queue:using:) method rather than the addObserver(_:selector:name:object:) method that is often used from Objective-C code. The advantage of the first variant is that you don't have to use the @objc attribute on your method:

    func batteryLevelChanged(notification: Notification) {
        // do something useful with this information
    }

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil,
        using: batteryLevelChanged)

and you can even just use a closure instead of a method if you want:

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil) { _ in print("🔋") }

You can use the returned value to stop listening for the notification later:

    NotificationCenter.default.removeObserver(observer)

There used to be another advantage in using this method, which was that it doesn't require you to use selector strings which couldn't be statically checked by the compiler and so were fragile to breaking if the method is renamed, but Swift 2.2 and later include #selector expressions that fix that problem.

Upvotes: 62

Deepak Thakur
Deepak Thakur

Reputation: 3701

In swift 2.2 - XCode 7.3, we use #selector for NSNotificationCenter

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)

Upvotes: 2

leanne
leanne

Reputation: 8749

I'm able to do one of the following to successfully use a selector - without annotating anything with @objc:

NSNotificationCenter.defaultCenter().addObserver(self,
    selector:"batteryLevelChanged:" as Selector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

OR

let notificationSelector: Selector = "batteryLevelChanged:"

NSNotificationCenter.defaultCenter().addObserver(self,
    selector: notificationSelector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

My xcrun version shows Swift 1.2, and this works on Xcode 6.4 and Xcode 7 beta 2 (which I thought would be using Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)

Upvotes: 3

Related Questions