Detect UIViewController changed in Swift - swift

For analytics purposes, I would like to detect whenever the top UIViewController is modified.
I don't want to use inheritance, but rather delegate some event that I can use.
I see that Firebase have some type of mechanism for that but I couldn't figure how exactly.
Something like the following will be great:
NotificationCenter.default.addObserver(self, selector: #selector(topViewModified), name: .TopViewModified, object: nil)
Thanks.

Related

NotificationCenter is observing multiple times

I'm having a problem in my implementation using observers in Swift, The problem is I'm having the same observer multiple times so I make one function more than once.
How can I validate if I'm observing the same observer already before observing it again?
NotificationCenter.default.addObserver(self, selector: #selector(myFunction(notification:)), name: NSNotification.Name(rawValue: "SameObserver"), object: nil)
Thanks for your help
By making sure to balance your calls to addObserver and removeObserver. Alternately, you could create a boolean somewhere that indicates that you've already called addObserver, but it's better just to balance your calls.
The best way to balance your calls is to tie them to the observer's lifetime or lifecycle. For lifetime you would call addObserver in init and removeObserver in deinit (or use one of the forms that automatically removes your observation). For lifecycle (for example in a ViewController), you would call addObserver in viewWillAppear and removeObserver in viewDidDisappear.
There is no mechanism for querying NotificationCenter.

how to reload NSTableView data from a different ViewController

I'm working on a simple notes app for macOS. I have a home page which is has a NSTableView that displays all your notes, when you click the new note button a new View appears where you can create a new note. Once you click the note it adds the new note to the database and should reload the table view data, but I need to stop the current run and run the program again to see the changes.
I used this post to achieve the same effect on iOS but it seems to not work on MacOS
So how do I adapt:
override func viewDidLoad() {
super.viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
}
In the home page VC
and the line:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
Inside of the saveNewNote IBAction to work in macOS? also are you even able to use the NotificationCenter in macOS apps or is it only on iOS?
NSNotificationCenter is part of Foundation framework, so it's definitely available on macOS.
You should be able to use it the same way you've been using it on iOS.
If you have an IBAction called saveNewNote, inside that method you can posy the notification the way you wrote.
In the view controller which owns the table, add the observer like you wrote, and reload the table...
If it doesn't work, we might need some code example of how you set it up on the Mac app the better understand what isn't working.

How to synchronize multiple controls?

I am looking for pattern in swift to synchronize the state of multiple controls. Say I have NSSegmentedControl in the view, NSMenuItems with checkmarks and NSSegmentedControl in touch bar. Each of them is sending change to model. But how to synchronize with other two? I can use delegates but it looks like the complexity will grow with number of synchronizable items. Is there a common pattern to approach this problem? I would think of something like subscription to change, so all 3 controls can subscribe to it and receive notification when a change occurs, then use it's own transformation of data to pass to the control. Wonder if there is some mechanism for that.
There is a mechanism for that, it's the notification model. I imagine you want the view controller to listen for changes and update its controls--not have each control listen for changes and update itself (which is also possible). The basic approach would be to add a notification observer to the view controller (or whatever object you want to be the observer)...
NotificationCenter.default.addObserver(self, selector: #selector(nHandler(_:)), name: NSNotification.Name(rawValue: "someControlDidChange"), object: segmentedControl)
...and a handler to take action on that notification...
#objc func nHandler(_ notification: Notification) {
guard let control = notification.object as? UISegmentedControl else {
return
}
// determine which control posted the notification
// read the control's new value
// update the other controls
}
Then as a control updates its values, it posts notifications...
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "someControlDidChange"), object: self)
The object here can be self if this notification is posted from within the control (i.e. it's subclassed) or the instance of the control itself (i.e. someSegmentedControl).
The notification observer and the notification poster allow you to include an optional object, as you see in my examples. You should be passing some data within these notifications to allow the observer to determine what object is posting the notification and what value that object has been updated to. Then in the handler of the notification observer, you unwrap that object to get that data.
I have yet to benchmark the costs of posting notifications; I really have no idea how expensive it is. If the delegate approach is too messy and you've written off property observers, the notification model is likely the way to go.

UIKeyboardWillChangeFrameNotification swift implementation

For a long while now, I've been looking for a Swift implementation that utilizes UIKeyboardWillChangeFrameNotification so far I haven't come across anything. I'm trying to get this working so that I can handle the keyboard show and hide in one method as stated in the documentation here and here written in objective-c.
and replace these:
// Register your Notification, To know When Keyboard appears.
NotificationCenter.default.addObserver(self, selector: #selector(myKeyboardWillShowMethod), name: .UIKeyboardWillShow, object: nil)
// Register your Notification, To know When Keyboard hides.
NotificationCenter.default.addObserver(self, selector: #selector(myKeyboardWillHideMethod), name: .UIKeyboardWillHide, object: nil)

Where is the right place to remove notification observers in Swift 2?

I have this code, taken from this answer: https://stackoverflow.com/a/29099066/406322
extension NSNotificationCenter {
func setObserver(observer: AnyObject, selector: Selector, name: String?, object: AnyObject?) {
NSNotificationCenter.defaultCenter().removeObserver(observer, name: name, object: object)
NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object)
}
}
Now, in my view controller, I am setting my observers in viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
setObservers()
}
func setObservers() {
NSNotificationCenter.defaultCenter().setObserver(self, selector: #selector(BaseController.handleComment(_:)), name: "newComment", object: nil)
}
However, even with using this extension, where the observer is removed before getting added, each time I exit the view controller, and return to it, I get multiple notifications (one extra each time).
How is this possible?
If you need this setObserver extension, you are very likely doing something wrong. You should be able to balance your registration and removal easily. If you can't, your notification management is very likely too complicated or in the wrong place.
Typically the correct place to add observations is in viewWillAppear (or viewDidAppear, either is fine), and remove them in viewDidDisappear (or viewWillDisappear). This ensures that you do not receive notifications while you are offscreen, even if the view controller still exists (which is common).
If your view controller requires that it receive notifications while it is offscreen, then you have a design problem. View controllers should only manage onscreen views. If they're doing anything else, you have put too much of the model into the controller.
As #rmaddy notes, your specific problem is likely that you have two instances of this view controller. That's may be fine or it might be a mistake (it depends on how the view controller works). But if you balance adding and removing your registration when going on and offscreen, that part will be fine.