ios swift 3 addObserver how to redirect to a certain view from anywhere in the app when push notification tapped - swift

I have an application which has tabBar with 5 tabs
first one let's call it "FirstView" - has following code:
override func viewWillAppear(_ animated: Bool) {
...
NotificationCenter.default.addObserver(self, selector: #selector(self.catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
...
NotificationCenter.default.removeObserver(self)
}
override func viewDidAppear(_ animated: Bool) {
...
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
}
}
I also have code in didReceiveRemoteNotification which ha this bit of code:
NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
And I got UIViewController extension where I have catchIt() and defining redirections logic:
func catchIt(_ userInfo: Notification) {
let notification = userInfo.userInfo
if (path == "account.balance") {
let vc: ProfileViewController = storyboard!.instantiateViewController(withIdentifier: "account.balance") as! ProfileViewController
self.navigationController?.pushViewController(vc, animated: true)
//..and so on for other paths
}
}
This code has 2 problems:
1) Redirection only works if the last opened View was FirstView. if it is other View then I will never get redirected when taping push notification
2) I got my history when it is redirected but it tracks all the way down till the FirstView even if the destination controller is on the other tab different from the FirstViews (I tried to redirect first to required tab and then to destination controller but still it gives the track from the FirstView's tab)
I also tried to use didReceiveRemoteNotification - wrote all the redirections logic into it but it did not work
Would highly appreciate your advice

The problem here, is that the post notification is called before the addObserver. That´s why nothing triggers.
SOLUTION :
Do not addObserver in your viewWillAppear but do it in the init method like this :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
//Redirect for push notification
NotificationCenter.default.addObserver(self, selector: #selector(self. catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}

Related

NotificationCenter - addObserver not called

I am trying a very simple code with NotificationCenter. But the addObserver is not getting called. Can any one of you check and let me know what i am missing. There are 2 simple class, one which post notification and another which listens to it. When i run the program, i just see "sending notification" in the console.
Thanks in advance.
Class 1:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("sending notification")
NotificationCenter.default.post(name: Notification.Name("test"), object: nil)
}
}
Class 2:
class secondvc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("second vc")
NotificationCenter.default.addObserver(self,
selector: #selector(doThisWhenNotify(_:)),
name: Notification.Name("test"),
object: nil)
}
#objc func doThisWhenNotify(_ notification: Notification) {
print("inside notification")
}
}
If, at the time ViewController comes into existence, secondvc does not yet exist, then there is no one there to receive the posted notification and that is why you don't see the notification being received later when secondvc does come into existence.

NotifcationCenter causing a strong reference cycle - Swift 5

It appears I am getting a Strong Reference Cycle when using the NotifcationCenter.
I am using NotificationCenter to observe the Rotation of the device. (While some would argue this is not the best way to determine the device rotation, this currently seems to be my only route, as no autolayout is being used and storyboard is not being used).
deinit {} is never being called in my ViewController even if I remove the observer in viewWillDisappear and viewDidDisappear.
import UIKit
class TestVC: UIViewController {
deinit {
print("TestClass Deinit") //not being triggered ever
}
#objc private func rotationDetected(sender: Any) {
print("we rotated")
}
override func viewDidDisappear(_ animated: Bool) {
//NotificationCenter.default.removeObserver(UIDevice.orientationDidChangeNotification)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(UIDevice.orientationDidChangeNotification)
//NotificationCenter.default.removeObserver(self) //also doesn't work
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Any ideas as to why this is occurring and how to resolve it?
Also open to any new ideas on how to determine rotation detection else ways (no autolayout or storyboard is being used though).
To reach TestVC() I used self.navigationController?.pushViewController(TestVC(), animated: true) in the previous ViewController and to go back I use the pop.
Without the Observer present, the class will correctly deinit.
RESOLVED
Thanks to the answer marked below the Strong Reference Cycle is removed.
Simply replace NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)
with
NotificationCenter.default.addObserver(self, selector: #selector(rotationDetected), name: UIDevice.orientationDidChangeNotification, object: nil)
This should work in viewWillDisappear:
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
in combination with using the following in viewWillAppear:
NotificationCenter.default.addObserver(self, selector: #selector(rotationDetected), name: UIDevice.orientationDidChangeNotification, object: nil)
your method of removing observer is incorrect, you should do like this:
class TestVC {
private var observer: Any
func viewWillAppear() {
observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)
}
func viewWillDisappear() {
NotificationCenter.default.removeObserver(observer)
}
}
to eliminate the Strong Reference Cycle, use weak in the closure.

prefersStatusBarHidden not updating after calling setNeedsStatusBarAppearanceUpdate()

Different vcs inside my app show the status bar visible and others are hidden. This is set to YES in the info.pList
"View controller-based status bar appearance": YES
// also tried togging this between yes and no
"Status bar is initially hidden": YES
The app has 2 windows, the main window and a second window. The second window gets presented it front of the main window on a button push. The vc in the second window has the status bar hidden.
The problem is if I'm on a vc (mainVC) inside the main window that shows the status bar, I press the button to show the second window, mainVC's status bar disappears. The second window gets presented and after I dismiss it I send a notification to mainVC to call setNeedsStatusBarAppearanceUpdate() but prefersStatusBarHidden isn't triggered so the status bar stays hidden even though it shouldn't be. I even subclassed a Navigation Controller and added the code there with mainVC as it's root.
Why isn't prefersStatusBarHidden getting called?
I added prefersStatusBarHidden inside the mainVC by itself, the nav to the mainVC by itself, and then in both the mainVC and it's nav at the same time. It's still not getting called after setNeedsStatusBarAppearanceUpdate() gets called in either places.
Subclassed nav:
class MainVCNavController: UINavigationController {
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)
}
let statusBarHidden: Bool = false
#objc func updateStatusBar() {
self.setNeedsStatusBarAppearanceUpdate() // this gets called when the notification is triggered
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden // this doesn't get called after setNeedsStatusBarAppearanceUpdate() is called
}
// I added this just to see if it would make a difference but it didn't
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
override open var childViewControllerForStatusBarStyle: UIViewController? {
return self.topViewController
}
override open var childViewControllerForStatusBarHidden: UIViewController? {
return self.topViewController
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
MainVC is the rootVC of the above nav
class MainVCController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)
}
let statusBarHidden: Bool = false
#objc func updateStatusBar() {
self.setNeedsStatusBarAppearanceUpdate() // this gets called when the notification is called
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden // this doesn't get called after setNeedsStatusBarAppearanceUpdate() is triggered
}
// I added this just to see if it would make a difference but it didn't
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
}
SecondVC inside the second window has it's status bar hidden. It sends the notification when it's dismissed to the above mainVC:
class SecondController: UIViewController {
override var prefersStatusBarHidden: Bool {
return true
}
if dismissed {
NotificationCenter.default.post(name: Notification.Name(rawValue: "updateStatusBar"), object: nil)
}
}
I also read that I need to call the below to trigger the prefersStatusBarHidden but even when I added these to the updateStatusBar() it didn't make a difference.
navigationController?.setNavigationBarHidden(false, animated: false)
// or
navigationController?.navigationBar.isHidden = false
Updating the status bar needs to be on the main thread.
There are two ways to ensure that:
Add notification observer on the main thread: (you don't need to expose the func to objc c):
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "updateStatusBar"), object: nil, queue: .main, using: updateStatusBar)
func updateStatusBar(_ notification: Notification) {
setNeedsStatusBarAppearanceUpdate()
}
Or update the status bar on the main thread:
NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar(_:)), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)
#objc func updateStatusBar(_ notification: Notification) {
DispatchQueue.main.sync {
self.setNeedsStatusBarAppearanceUpdate()
}
}
#zombie’s answer about the updating on the main thread 100% worked. On another note he also suggested I use symbolic breakpoints to diagnose the problem. He provided a great link to help:
Symbolic Breakpoints

NsNotificationCenter is not working

Actually i'm new in swift i got stuck here,Anyone can solve this Problem. actOnSpecialNotification Func is not calling on fireNotification in ViewController.swift
In ViewController.swift
func fireNotification() -> Void {
NotificationCenter.default.addObserver(self, selector:
#selector(vikas.updateNotificationSentLabel), name:
NSNotification.Name(rawValue: mySpecialNotificationKey), object: nil)
}
func updateNotificationSentLabel() {
print("sent")
}
in SecondVC.swift
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:
#selector(ViewController.actOnSpecialNotification), name:
NSNotification.Name(rawValue: mySpecialNotificationKey), object: nil)
}
func actOnSpecialNotification() {
print("listen")
}
First of all add Observer to your FirstViewConroller.
FirstViewConroller
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:”test”, object: nil)
Now, add the relevant selector method in same ViewController which will be called once the notification will be fired.
func methodOfReceivedNotification(notification: Notification){
//Take Action on Notification
}
Now, you can fire the notification using below lines which will call the above method which resides in FirstViewController
SecondViewController
NSNotificationCenter.defaultCenter().postNotificationName(“test”, object: nil)

How is that possible to post notification after 1 second delay or in a dispatch asyn?

i just want to call a method after loading my view. i have set notification to one view and postnotification to other view you will get more idea from my code.. here is my code..
in a one.swift file
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "InsertNote:", name: "insert", object: nil)
}
func InsertNote(notification:NSNotification)
{
println("blablalbla")
}
in a second.swift file
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
}
func getData()
{
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
i have try also in a dispatch_asynch but still it is not work.
Try this
let delayInSeconds: Double = 1
override func viewDidLoad() {
super.viewDidLoad()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue()){
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
}
The only reason I can come up with that this doesn't work, is that your first ViewController A is already deallocated when you send the notification with the second ViewController B. So, you are sending the notification with B correctly, but A isn't around to receive it.
You can test this by adding an observer in B as well.
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "insertNote:", name: "insert", object: nil)
}
func getData() {
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
func insertNote:(note: NSNotification) {
print("Banana")
}
// Should print 'Banana' after one second.