Notification Center Observer Selector Method does not get called - ios

I am adding an observer on A view controller and view controller B is presenting on A.
While dismissing Controller B , I have posted the notification but It does not called the selector method added in A.
Also notification get registered first and the post method is get called. I have already checked that.
Here is the sample code:
NotificationCenter.default.addObserver(self, selector: #selector(closButtonPressed(notification:)) ,name: Notification.Name("CloseButtonPressed"), object: nil)
#objc func closButtonPressed(notification: Notification){
}
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"), object: self)
Any help would be appreciated.

Make sure posting notification in completion handler
self?.dismiss(animated: true, completion: {
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"),
object: self)
}

Make sure you implement Notification correctly. I recommend you to create an extension for Notification Name & make it static.
extension Notification.Name {
static let didTapCloseButton = Notification.Name("CloseButtonPressed")
}
NotificationCenter.default.addObserver(self, selector: #selector(didTapCloseButton(_:)), name: .didTapCloseButton, object: nil)
#objc func didTapCloseButton(_ sender: Notification?) {
}
NotificationCenter.default.post(name: .didTapCloseButton, object: nil)

I am not sure what's wrong with your project. To solve your problem, I create a test project and write some code like this:
//ControllerA.swift
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(getNotification(notification:)), name: NSNotification.Name("CloseButtonPressed"), object: nil)
}
getNotification(notification: Notification) {
print(notification)
}
#objc func buttonAClick() {
navigationController?.present(ViewControllerB(), animated: true, completion: {
})
}
//ViewControllerB.swift
#objc func buttonClick() {
NotificationCenter.default.post(name: Notification.Name("CloseButtonPressed"), object: self)
self.dismiss(animated: true) {
}
}
As you said, I add notification in ControllerA, and present ControllerB, When ControllerB close, post notification and dismiss, console can print the notification object, so may I miss something?

Related

Update variables when day changes Swift [duplicate]

I want my app to act when there is a change to another day.
So, in my appDelegate, I put
func applicationSignificantTimeChange(_ application: UIApplication){
//this one fires
}
and in the ViewController that should update its content I do:
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(self.dayChanged(notification:)), name: Notification.Name("significantTimeChangeNotification"), object: nil)
}
and
#objc func dayChanged(notification: NSNotification){
//this one doesn't fire
}
somehow, while the func in AppDelegate is called, the observer seems to be blind for that event.
Is this syntax, or just plain misunderstanding of the mechanism?
You need to add an observer for "UIApplicationSignificantTimeChangeNotification":
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: UIApplicationSignificantTimeChangeNotification, object: nil)
For Swift 4.2 or later
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: UIApplication.significantTimeChangeNotification, object: nil)
Note: If your intent is to be notified when the day changes you can use .NSCalendarDayChanged ("NSCalendarDayChangedNotification") instead of UIApplication.significantTimeChangeNotification.
NotificationCenter.default.addObserver(self, selector: #selector(dayChanged), name: .NSCalendarDayChanged, object: nil)
And add the selector method to the view controller where you would like to monitor the day changes:
#objc func dayChanged(_ notification: Notification) {
}

Function returning emptying String when using Notification and observers?

In the following code I want to return a string from first controller to third controller. But it returns an empty string, when trying by notification and observers.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil)
}
#objc func token (notification:Notification) -> String!{
return self.token! //return token
}
extension Notification.Name {
static let token = Notification.Name("Token")
}
ThirdViewController*
override function ViewDidLoad(){
let token = NotificationCenter.default.post(name: .token, object: nil)
print(token) // () printing empty
}
If I understood you problem correctly you want to pass object from first controller to third, you can use segue for it. This is the example how you can pass it to second, the same thing to pass forward from second to third
#IBAction func goForawrd(_ sender: UIButton) {
performSegue(withIdentifier: "second", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "second" {
let vc = segue.destination as? SecondViewController
vc?.object = yourObjectYouWantToPass
}
}
or if you don't want to use segue you can use next code
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(withIdentifier: "second")
secondVC.object = yourObjectYouWantToPass
don't forget to set viewController identifier before
This
NotificationCenter.default.post(name: .token, object: nil) doesn't return anything you get the observer wrongly it posts the notification and if there is an observer it'll be forwarded to it , so this flow occurs
1-
NotificationCenter.default.post(name: .token, object: nil)
2-
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil
3-
#objc func token (notification:Notification) {}
if you need to send data from first to second set it when you segue/present/push , if you need to send data from second to first use a delegate
Okay so you are printing nil since that is not the value of the token just the reference for the post method.
Since it is not a bidirectional thing you cannot retrieve the value there.
If you want to achieve this by notifications these are the required steps:
Send a notification about you need a token
When notification arrived on first controller grab what you need and send another notification with the value you need, and handle it where you need.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenGet, object: nil)
}
#objc func token (notification:Notification) {
NotificationCenter.default.post(name: .tokenSet, object: token)
}
extension Notification.Name {
static let tokenGet = Notification.Name("TokenGet")
static let tokenSet = Notification.Name("TokenSet")
}
ThirdViewController*
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenSet, object: nil)
NotificationCenter.default.post(name: .tokenGet, object: nil)
}
func tokenSet(notification: Notification) {
/// here you can get the value from notification
}
Note that i would NOT do in this way. Pass the token through the view controllers or create a class which is responsible for token handling and pass that around.

How to register Notification Center in class iOS

I am new to ios programming, I wanted to register notification center in the class, not in the view controller, I want to send some action from one view controller this custom class receives that action and it performs some view controller navigation.I wanted to have custom notification center in ios.
code:
NotificationCenter.default.post(name: Notification.Name(rawValue: "key"), object: nil)
class MainReceiver: NotificationCenter
{
override func post(name aName: NSNotification.Name, object anObject: Any?)
{
print("coming here")
}
}
Here you have snipped code:
class MainReceiver {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(handleMethod(_:)), name: NSNotification.Name(rawValue: "Notification_Name"), object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func handleMethod(_ notification: Notification) {
// handle
}
}
From Apple's NSNotificationCenter documentation:
Each running Cocoa program has a default notification center. You typically don’t create your own.
I'm not sure why you think you want to create your own notification center, but trust me, you don't.
However, you can easily subscribe an object (an instance of a custom class, like you say you want to use) to notifications:
// let object = Whatever()
NotificationCenter.default.addObserver(object, selector: #selector(didReceive(notification:)), name: NSNotification.Name(rawValue: "key"), object: nil)
// object's didReceive(notification:) function will now be called when a notification with the specified name is posted. This can be posted from anywhere.
A while back, the function had to be decorated with the #objc attribute. I'm not sure if that's still the case, but if so, you'd use #objc func didReceive(notification: Notification) as your declaration in the receiving class.
If I understand your question correctly I think what you are looking for is the blocked based variant for the NotificationCenter.
You can find it in the official docs.
Example code from the docs:
let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
self.localeChangeObserver = center.addObserverForName(NSCurrentLocaleDidChangeNotification, object: nil, queue: mainQueue) { (note) in
print("The user's locale changed to: \(NSLocale.currentLocale().localeIdentifier)")

iOS unable to remove Notification observer. Deinit not getting called

I have a UIView similar to the one you can see below:
class ViewTaskViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
subscribeToNotifications()
}
func subscribeToNotifications() {
let notification = NotificationCenter.default
notification.addObserver(forName: Notification.Name(rawValue: "TimerUpdated"), object: nil, queue: nil, using: handleUpdateTimer)
print("Subscribed to NotificationCenter in ViewTaskViewController")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("TUFU TUFU TUFU")
NotificationCenter.default.removeObserver(self)
}
deinit {
print("DENINT")
}
#objc func handleUpdateTimer(notification: Notification) {
if let userInfo = notification.userInfo, let timeInSeconds = userInfo["timeInSeconds"] as? Int {
withUnsafePointer(to: &self.view) {
print("We got timeeeeee \(timeInSeconds) \($0)")
}
//do something here....
}
}
}
The issue I am having is that I am unable to remove the observers from this particular UIView when the user hits the back button and returns to another viewController.
ViewWillDisppear is called but deinit is not called. The strange thing is that if we remove subscribeToNotifications() from viewDidLoad() then the deinit is called.
The other issue is related to a memory leak. As you can see in the screenshot below, when the view does subscribe to notifications and the user leaves/re-enters the view, the memory usage increase.
Now compare that to when the subscribeToNotifications() is commented out, there is no increase in memory usage and only one instance of the viewController.
The conclusion is that there seems to be a correlation between the notification subscription creation of a new instance of the UIView hence the deinit is not being called.
I'd like to find out if there is a way we can deinitialize the view and unsubscribe from the notification.
Please let me know if you need further information. :)
I've found the removeObserver() only works if you use this version of addObserver()
notification.addObserver(self, selector:#selector(self.handleUpdateTimer), name: Notification.Name(rawValue: "TimerUpdated"), object: nil)
I'm guessing with the original version you aren't actually indicating who the observer is.
As #Spads said you can use
NotificationCenter.default.addObserver(self, selector: #selector(subscribeToNotifications), name: NSNotification.Name(rawValue: "TimerUpdate"), object: nil)
or the one you already have.
you can remove your notification by it's name or it's reference
NotificationCenter.default.removeObserver(self, name: "TimerUpdate", object: nil)
if you declared your notification at the top of your class then you can directly pass the reference of your notification to be removed in your case notification
NotificationCenter.default.removeObserver(notification)
You should store your newly added observer in a opaque object (NSObjectProtocol) and then call NotificationCenter.default.removeObserver(self.nameOfObserver)

NotificationCenter Crash in Swift 3

Is it just me, or did NotificationCenter become a hot mess in Swift 3? :)
I have the following setup:
// Yonder.swift
extension Notification.Name {
static let preferenceNotification = Notification.Name("preferencesChanged")
}
// I fire the notification elsewhere, like this:
NotificationCenter.default.post(name: .preferenceNotification, object: nil)
In my first view controller, this works great:
// View Controller A <-- Success!
NotificationCenter.default.addObserver(self, selector: #selector(refreshData), name: .preferenceNotification, object: nil)
func refreshData() {
// ...
}
But this view controller:
//View Controller B <-- Crash :(
NotificationCenter.default.addObserver(self, selector: #selector(loadEntries(search:)), name: .preferenceNotification, object: nil)
func loadEntries(search:String?) {
// ...
}
...crashes with:
[NSConcreteNotification length]: unrecognized selector sent to instance
As far as I can tell, my observer is set up correctly. Any idea what I'm doing wrong?
Your issue is with your loadEntries(search:) method. It's not a valid signature. The selector used with Notification Center must either have no parameters or just one parameter. And if you have one parameter, that parameter will be the Notification object, not the notification name.
Your loadEntries needs to be:
func loadEntries(_ notification: NSNotification) {
// Optional check of the name
if notification.name == .preferenceNotification {
}
}
And the selector would need to be:
#selector(loadEntries(_:)) // or #selector(loadEntries)

Resources