NSNotificationCenter to refresh data not moving past addObserver - ios

I need to use NSNotificationCenter to update me when new data comes in from WCSession so I can fill out my tableview.
I'm setting breakpoints at each of the functions listed, but for some reason it goes right to NSNotificationCenter.defaultCenter().addObserver(self, selector: "loadList:", name:"load", object: nil) and then no where else.
So something isn't right, can you help me out? Thanks!
DataManager:
class DataManager : NSObject, WCSessionDelegate {
func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
if let tColorValue = userInfo["TeamColor"] as? String, let matchValue = userInfo["Matchup"] as? String {
receivedData.append(["TeamColor" : tColorValue , "Matchup" : matchValue])
evnts.append(Evnt(dataDictionary: ["TeamColor" : tColorValue , "Matchup" : matchValue]))
self.dataObjects = evnts
NSNotificationCenter.defaultCenter().postNotificationName("load", object: nil)
} else {
print("not same as dictionary value")
}
}
InterfaceController:
override init() {
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "loadList:", name:"load", object: nil)
}
func loadList(notification: NSNotification){
//load table data here
doTable()
}
func doTable() {
self.rowTable.setNumberOfRows(DataManager.sharedInstance.dataObjects.count, withRowType: "rows")
for (index, evt) in DataManager.sharedInstance.dataObjects.enumerate() {
if let row = rowTable.rowControllerAtIndex(index) as? TableRowController {
row.mLabel.setText(evt.eventMatch)
} else {
print("nope")
}
}
}

DataManager:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil, userInfo: [String : AnyObject])
InterfaceController:
NotificationCenter.default.addObserver(self, selector: #selector(doThisWhenNotify(notification:)), name: NSNotification.Name(rawValue: "load"), object: nil)
//........
func doThisWhenNotify(notification : NSNotification) {
let info = notification.userInfo
//load your stuff here
}

Related

Notification observer selector not called

I have this observer
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(GetUserID(_:)), name: Notification.Name("UserID"), object: nil)
}
and this selector function
#objc func GetUserID(_ notification: Notification){
let User = notification.object as? String
self.UserID = User
}
And I keep getting nil in UserID even though I am certain that when I get the notification it is not nil which makes me believe that the selector is not being called
You can get observer value by below method
let selectedIndex:[String: Int] = ["selectedIndex": 1]
//selectedindex = 0
// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SelectedSegment"), object: nil, userInfo: selectedIndex)
NotificationCenter.default.addObserver(self, selector: #selector(self.changeSegmentControlIndex(notification:)), name: Notification.Name("SelectedSegment"), object: nil)
#objc func changeSegmentControlIndex(notification: Notification) {
if let selectedIndex = notification.userInfo?["selectedIndex"] as? Int {
// do something with your image
print(selectedIndex)
}
}

NotificationCenter is not calling the selector

I've searched about this but the problem still exist for me. I found this great question but unfortunately it didn't work for me. This is the first time I'm working with NotificationCenter and the need to use this first occurs when I wanted to pass data to a viewcontroller under a tab of XLPagerTabStrip.
So here is how I am posting the Notification:
if let doc_ID = mainDoctorsArray[sender.tag].doctors_id {
NotificationCenter.default.post(name: Notification.Name("docID"), object: nil, userInfo: ["value" : doc_ID])
}
In the class I've made for observing this notification I'm calling NotificationCenter.default.addObserver(self, selector: #selector(gotDocID), name: Notification.Name("docID"), object: nil)
The selector method is:
func gotDocID(notification:NSNotification) {
let userInfo:Dictionary<String,String> = notification.userInfo as! Dictionary<String,String>
if let item = userInfo["value"] {
getDoctorDetails(docID: Int(item)!)
//print(item,self)
}
}
I've also tried adding observer as:
NotificationCenter.default.addObserver(self, selector: #selector(AvailableViewController.gotDocID(notification:)), name: Notification.Name("docID"), object: nil) but still same result.
The issue is that func gotDocID(notification:NSNotification) is not being called.
UPDATE
Class which is posting the notification is ViewController.swift and the class which has the observer is AvailableViewController.swift
Based on a comment I've changed observer to NotificationCenter.default.addObserver(self, selector: #selector(AvailableViewController.gotDocID(notific‌​ation:)), name: Notification.Name("NotificationIdentifier"), object: nil) and this error is generated.
and also by doing the follow I'm getting the same error.
Value of type 'AvailableViewController' has no member 'gotDocID'
You can use the below code to post and get data.
//Post notification
NSNotificationCenter.defaultCenter().postNotificationName("docID", object: nil, userInfo: ["value" : doc_ID])
//Get data from observer
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AvailableViewController.gotDocID(_:)), name: "docID", object: nil)
//Method called after notification is posted.
func gotDocID(notification: NSNotification) {
if let image = notification.userInfo?["value"] as? String {
// do something with your data
}
}
Please check :
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.gotDocID(notification:)), name: Notification.Name("docID"), object: nil)
}
#IBAction func saveButton(_ sender: UIButton) {
NotificationCenter.default.post(name: Notification.Name("docID"), object: nil, userInfo: ["value" : "123"])
}
#objc func gotDocID(notification:NSNotification) {
let userInfo:[String: String] = notification.userInfo as! [String: String]
if let item = userInfo["value"] {
print(item,self)
}
}
}
Add #objc to your function
#objc func gotDocID(notification:NSNotification) {
}
// Define identifier
let notificationName = Notification.Name("docID")
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(AvailableViewController.gotDocID(notification:)), name: notificationName, object: nil)
// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)
// Stop listening notification
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil);
Why dont you try closure
Make sure your post notification occures.
Change
NotificationCenter.default.post(name: Notification.Name("docID") , object: ["value" : doc_ID])
NotificationCenter.default.addObserver(forName: Notification.Name("docID"), object: nil, queue: OperationQueue.main) { (notify) in
print(notify.object as! Dictionary<String,String>)
}

How to unregister an NSNotification in Swift iOS

I have two controllers
class CtrlA: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(CtrlB.self, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
class CtrlB: UIViewController {
static func badge (notification: NSNotification) {
// blah blah
}
}
Whats the correct way to unregister the notification listener above?
I'm not certain this is correct:
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
I don't think I can use self either, since it was registered on CtrlB.self
So the best way to implement the notification in your project is create one class called NotificationManager inside that declare one dictionary in which you can always update the observers
class NotificationManager {
var observers = [String: AnyObject]()
}
Create addObserver method, post notification method and remove
observer method inside the same class.
func postNotification(_ name: String, userInfo: [AnyHashable: Any]? = nil) {
NotificationCenter.default.post(name: name, object: nil, userInfo: userInfo)
}
func addObserver(_ name: String, block: #escaping (Notification) -> Void) {
//if observer is already in place for this name, remove it
removeObserverForName(name)
let observer = NotificationCenter.default.addObserver(forName: name), object: nil, queue: OperationQueue.main, using: block)
self.observers[name] = observer
}
func removeObserver(_ name: name) {
guard let observer = self.observers[name] else { return }
NotificationCenter.default.removeObserver(observer)
self.observers.removeValue(forKey: name)
}
//Removes all observers
func removeAllObservers() {
for observer in self.observers.values {
NotificationCenter.default.removeObserver(observer)
}self.observers = [:]
}
So access the above method in any of your class wherever its required and it will take care of everything. This will also prevent crash in your code. If try to remove the same observer more than one time.
I am not sure why you are registering/unregistering to notifications with a class and not an instance. 'CtrlB.self' - will not give you an instance of the CtrlB class, in fact it will return a class itself.
Instead you should use something like this:
class CtrlA {
let ctrlBInstance = CtrlB()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(ctrlBInstance, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(ctrlBInstance, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
And your ClassB should look like this in this case:
class CtrlB {
func badge (notification: NSNotification) {
// blah blah
}
}
You need to get the instance of the observer,which you haven't declared...
for instance you need to set class variable secondA...
class CtrlA: UIViewController {
var secondController: CtrlB?
override func viewDidLoad()
{
super.viewDidLoad()
if let unwrappedController = storyboard.instantiateViewController(withIdentifier: "someViewController") as? CtrlB
{
secondController = unwrappedController
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let secondController = secondController
{
NotificationCenter.default.addObserver(CtrlB.self, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let secondController = secondController
{
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
//Also don't forget to remove listening on deinit
deinit
{
if let secondController = secondController
{
NotificationCenter.default.removeObserver(secondController, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
}
class CtrlB: UIViewController {
//Here you go with notification...
static func badge (notification: NSNotification) {
// blah blah
}
}

NotificationCenter to pass data in Swift

I am working on a test project in Swift 3. I am trying to pass textField string from one class to another class using NotificationCenter. I am trying to workout the answer from this link: pass NSString variable to other class with NSNotification and how to pass multiple values with a notification in swift
I tried few answers from the above link but nothing worked.
My code:
//First VC
import UIKit
extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendData(_ sender: AnyObject) {
let userInfo = [ "text" : textView.text ]
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)
}
}
//SecondVC
import Foundation
import UIKit
class viewTwo: UIViewController {
#IBOutlet weak var result: UILabel!
override func viewDidLoad() {
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: .myNotificationKey, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .myNotificationKey, object: nil)
}
func notificationReceived(_ notification: Notification) {
guard let text = notification.userInfo?["text"] as? String else { return }
print ("text: \(text)")
result.text = text
}
}
I am not sure whats wrong with the code. Above, code originally marked as a answered which, I found from the first link. Code been converted to Swift.
Don't use object parameter to pass data. It is meant to filter notifications with the same name, but from a particular object. So if you pass some object when you post a notification and another object when you addObserver, you won't receive it. If you pass nil, you basically turn off this filter.
You should use userInfo parameter instead.
First, it is better to define notification's name as extension for Notification.Name. This approach is much safer and more readable:
extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}
Post notification:
let userInfo = [ "text" : text ]
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)
Subscribe:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: .myNotificationKey, object: nil)
}
Unsubscribe:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .myNotificationKey, object: nil)
}
Method to be called:
func notificationReceived(_ notification: Notification) {
guard let text = notification.userInfo?["text"] as? String else { return }
print ("text: \(text)")
}
Pass text using userInfo which is a optional Dictionary of type [AnyHashable:Any]? in Swift 3.0 and it is [NSObject : AnyObject]? in swift 2.0
#IBAction func sendData(_ sender: UIButton) {
// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: ["text": textValue.text])
print(textValue) // textValue printing
}
in viewDidLoad
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(self. incomingNotification(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
and in incomingNotification
func incomingNotification(_ notification: Notification) {
if let text = notification.userInfo?["text"] as? String {
print(text)
// do something with your text
}
}
In your sendData method pass textField.text into Notification object and in your incomingNotification do this:
guard let theString = notification.object as? String else {
print("something went wrong")
return
}
resultLabel.text = theString
You can also use blocks to pass data between controllers.
Use dispatchQueue because your notification is posting before your view load. Therefore just give delay in your notification Post.
#IBAction func sendData(_ sender: AnyObject) {
let userInfo = [ "text" : textView.text ]
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo) }
}
Just use NotificationCenter to send and receive the notification that state changed. Pass the data through some a data model such as an ObservableObject (particularly if you're bridging between SwiftUI and UIKit). Here's are a couple of extension that make it pretty simple for lightweight inter-component signaling without the cumbersome forgettable semantics of NotificationCenter. (Of course you define your own Notification.Name constants to be meaningful to your purpose).
extension Notification.Name {
static let startEditingTitle = Notification.Name("startEditingTitle")
static let stopEditingTitle = Notification.Name("stopEditingTitle")
}
extension NotificationCenter {
static func wait(_ name : Notification.Name) async {
for await _ in NotificationCenter.default.notifications(named: name) {
break;
}
}
static func post(_ name : Notification.Name) {
NotificationCenter.default.post(name: name, object: nil)
}
#discardableResult static func postProcessing(_ name: Notification.Name, using block: #escaping (Notification) -> Void) -> NSObjectProtocol {
NotificationCenter.default.addObserver(forName: name, object: nil, queue: OperationQueue.main, using: block)
}
}
To post a notification is as simple as:
NotificationCenter.post(.startEditingTitle)
And to receive the notification elsewhere:
NotificationCenter.postProcessing(.startEditingTitle) (_ in {
print("Started editing title")
}
Or to just wait for the notification instead of asynchronously handling it:
NotificationCenter.wait(.startEditingTitle)

How to pass data using NotificationCenter in swift 3.0 and NSNotificationCenter in swift 2.0?

I'm implementing socket.io in my swift ios app.
Currently on several panels I'm listening to the server and wait for incoming messages. I'm doing so by calling the getChatMessage function in each panel:
func getChatMessage(){
SocketIOManager.sharedInstance.getChatMessage { (messageInfo) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//do sth depending on which panel user is
})
}
}
However I noticed it's a wrong approach and I need to change it - now I want to start listening for incoming messages only once and when any message comes - pass this message to any panel that listens to it.
So I want to pass the incoming message through the NSNotificationCenter. So far I was able to pass the information that something happened, but not pass the data itself. I was doing that by:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.showSpinningWheel(_:)), name: showSpinner, object: nil)
then I had a function called:
func showSpinningWheel(notification: NSNotification) {
}
and any time I wanted to call it I was doing:
NSNotificationCenter.defaultCenter().postNotificationName(hideSpinner, object: self)
So how can I pass the object messageInfo and include it in the function that gets called?
Swift 2.0
Pass info using userInfo which is an optional Dictionary of type [NSObject : AnyObject]?
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
// For swift 4.0 and above put #objc attribute in front of function Definition
func showSpinningWheel(_ notification: NSNotification) {
if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}
Swift 3.0, 4.0, 5.0 version and above
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
// For swift 4.0 and above put #objc attribute in front of function Definition
func showSpinningWheel(_ notification: NSNotification) {
if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}
NOTE: Notification “names” are no longer strings, but are of type Notification.Name, hence why we are using NSNotification.Name(rawValue: "notificationName") and we can extend Notification.Name with our own custom notifications.
extension Notification.Name {
static let myNotification = Notification.Name("myNotification")
}
// and post notification like this
NotificationCenter.default.post(name: .myNotification, object: nil)
For Swift 3
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) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let id = dict["image"] as? UIImage{
// do something with your image
}
}
}
For Swift 4
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
#objc func showSpinningWheel(_ notification: NSNotification) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let id = dict["image"] as? UIImage{
// do something with your image
}
}
}
Hello #sahil I update your answer for swift 3
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) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let id = dict["image"] as? UIImage{
// do something with your image
}
}
}
Hope it's helpful. Thanks
This is what worked for me in Swift 5
NotificationCenter.default.addObserver(self,
selector: #selector(handleMassage),
name: Notification.Name("NotificationName"),
object: nil)
The method that handles the notification:
#objc func handleMassage(notification: NSNotification) {
if let dict = notification.object as? NSDictionary {
if let myMessage = dict["myMessage"] as? String{
myLabel.text = myMessage
}
}
}
I posted it like this:
let dic = ["myMessage": "testing"]
NotificationCenter.default.post(name: Notification.Name("NotificationName"), object: dic)
Swift 5.5 with avoid #selector():
First of all, declare the name:
extension Notification.Name {
static let prettyName = Notification.Name("MyPrettyName")
}
Next is to add observer (take care with queue):
// For example transferred data should implement protocol
protocol PrettyDelegate {
func doSomethingAwesome()
}
// Here is the way how we can subscribe as observer
NotificationCenter.default.addObserver(forName: .prettyName, object: nil, queue: nil) { [weak self] notif in
guard let self = self else { return } // Because self used more than once
if let userInfo = notif.userInfo,
let delegate = userInfo["pretty"] as? PrettyDelegate {
self.delegate = delegate
}
self.makePretty() // Here we can do anything
}
Finally, we can post the notification from another object
// Here I'll illustrate how to pass optional value as userInfo
var userInfo: [AnyHashable : Any]?
if let prettyDelegate = self as? PrettyDelegate {
userInfo = ["pretty": prettyDelegate]
}
NotificationCenter.default.post(name: .prettyName, object: nil, userInfo: userInfo)
NOTE: Do NOT forget to remove the observer, when you will not need it anymore.
this is how I implement it .
let dictionary = self.convertStringToDictionary(responceString)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SOCKET_UPDATE"), object: dictionary)
In swift 4.2 I used following code to show and hide code using NSNotification
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardheight = keyboardSize.height
print(keyboardheight)
}
}

Resources