Difference between NSNotification and NSEvent - objective-c

I don't really understand the difference that cocoa makes between a notification and an event.
For instance I could have code like this:
-(void)mouseMoved:(NSEvent*)event { … }
but not
-(void)windowMoved:(NSEvent*)event { … }
For the second one I'd have to use NSNotification – why?

The difference is, that NSEvent is used to encapsulate input events. Mouse down, key down etc.
However, NSNotification is used to notify observers about a change of a state or an object (eg. when the network reachability changed, new data became available or that a window moved).
In your case: A window move isn't some kind of input, but a change of the windows position. Thus you get an NSNotification rather an NSEvent.

Related

How do I explicitly send key-value observing notifications from an NSResponder?

I'm trying to make a NSTouchBar in an SDL application and I need to attach a responder to the NSWindow object (that's the only access SDL gives into the Cocoa windowing system).
https://developer.apple.com/reference/appkit/nstouchbar
If you explicitly adopt the NSTouchBarProvider protocol in an object,
you must also explicitly send the associated key-value observing
notifications within NSTouchBar methods; this lets the system respond
appropriately to changes in the bar.
What does that mean and how do I do it? I see lots of documentation about how to subscribe to the notifications, but not how to send them?
Right now I have:
#interface MyTouchBarResponder : NSResponder <NSTouchBarDelegate>
- (id)init;
- (NSTouchBar *)makeTouchBar;
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier;
#property(strong, readonly) NSTouchBar *touchBar;
#end
and I'm attaching it to the window with the code from a previous question I asked here: How to create an NSTouchBar from an NSWindow object?
touchBarResponder.nextResponder = window.nextResponder;
window.nextResponder = touchBarResponder;
but my callbacks aren't ever being called (I put exit(0) in them to make it very obvious). When I hack the code directly into the SDL library, things work as expected, but that's not a viable permanent solution.
Thank you.
First, your custom responder should conform to NSTouchBarProvider (in the above, you declare the touchBar property, but not the explicit conformance)
Second, you want to make sure that your custom responder is in the responder chain of the window (whether the first responder or just later in the chain). After adjusting the responder chain with your above code, you want to call -makeFirstResponder: and pass in some view in the window (if you need that view to be first responder) or with the custom responder object. You should then verify that the window's firstResponder is that object.
With these in place, you should get at least one call to touchBar after the window is shown and made key.
To answer the question on key-value observing notifications, that is needed for when you want to change the actual NSTouchBar object being returned from touchBar. In the general case this isn't necessary, since it's unnecessary in the static touch bar case, and even in the dynamic case, you can rely on just setting the defaultItemIdentifiers on the previously created touch bar and it will update. However, should you need to change the touch bar object, you need to ensure that -willChangeValueForKey: and -didChangeValueForKey: are sent for touchBar when you change the return value. This developer documentation on KVO goes into much more detail.

Detect user activity in Cocoa app (taps, clicks, ...)

For a Mac application, I want to detect user activity in the app, so I can periodically let a web service know that the user is still active on the endpoint.
In Cocoa Touch, I would override sendEvent of UIApplication, but the sendEvent in NSApplication equivalent in Cocoa, doesn't do the same.
Which APIs should I use instead for a Mac application, to detect user activity? Can I perhaps somehow have a global responder hookup from where I can send the pings to my service?
Preferably, I want to listen for actions the user can be expected to perform every 15-30 second, ie. clicks, tabs, typing, switching windows or applications.
You most likely want to create a global event monitor using +[NSEvent addGlobalMonitorForEventsMatchingMask:handler:]. This calls your handler whenever an event whose type matches the passed mask (you should use NSAnyEventMask) is sent to another application. You can observe, but not change, the event here, which suits your usage perfectly. There is one thing to watch out for: the documentation says that you won't receive key events unless your app is trusted for Accessibility.
You can do similarly for events that are routed to your own application with +[NSEvent addLocalMonitorForEventsMatchingMask:handler:].
It's not a notification, but you can query the time since user activity using CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType).
This worked for me:
-(void) addMyApplicationEventsMonitor {
self.localEventsMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskAny handler:^NSEvent * (NSEvent * event) {
// do your stuff here
return event;
}];
}
-(void)dealloc{
//remember add this to remove your monitor
[NSEvent removeMonitor:self.localEventsMonitor];
}
reference here

Is this a valid use of NSNotificationCenter

I come from a .NET web application background and have just started iOS development. The initial design of my app focuses around the NSNotificationCenter. I was reasonably happy with this until I saw various posts explaining how reaching for the NSNotificationCentre was a common newbie mistake.
Here is a simplified version of the problem I am trying to address:
My application is trying to show a list of messages that are populated using web service calls, think Facebook messaging.
When the app is first loaded it pulls a collection of messages from the server and displays them in a table to the user. The user can add new messages (which get sent back over the API) and the app can receive Push Notifications about new messages which are added to the feed.
The messages are never persisted to disk so I'm just using POCOs for the model to keep things simple.
I have a MessageFeedController which is responsible for populating the message feed view (in a storyboard). I also have a message feed model, which stores the currently retrieved values and has various methods:
(void) loadFromServer;
(void) createMessage: (DCMMessage*) message;
(void) addMessage: (DCMMessage*) message;
(NSArray*) messages;
(int) unreadMessages;
The current implementation I have is this:
Use case 1 : Initial Load
When the view first appears the "loadFromServer" method is called. This populates the messages collection and raises an NSNotificationCenter event.
The controller observes this event, and when received it populates the tableview
Use Case 2: New Message
When a user clicks the "add" button a new view appears, they enter their message, hit send and then the view is dismissed.
This calls the createMessage method on the model, which calls the API
Once we have a response the model raises the NSNotificationCenter event
Once again the MessageFeedController listens for this event and re-populates the table
Use Case 3: Push Message
A push notification is received while the app is open, with the new message details
The AppDelegate (or some other class) will call the addMessage method on the model, to add it to the collection
Once again, assuming the MessageFeed view is open, it re-populates
In all three cases the MessageFeed view is updated. In addition to this a BadgeManager also listens to these events which has the responsibility of setting the app icon badge and the tabbar badge, in both cases the badge number relates to the number of unread messages.
It's also possible that another view is open and is listening to these events, this view holds a summary of messages so needs to know when the collection changes.
Right, thanks for sticking with me, my question is: Does this seem like a valid use of NSNotificationCentre, or have I misused it?
One concern I have is that I'm not 100% sure what will happen if the messages collection changes half-way through re-populating the message table. The only time I could see this happening is if a push notification was received about a new message. In this case would the population of the table have to finish before acting upon the NSNotification anyway?
Thanks for your help
Dan.
In other words, you're posting a notification whenever the message list is updated. That's a perfectly valid use of NSNotificationCenter.
Another option is to use Key-Value Observing.
Your controller (and anyone else) can register as an observer to the "messages" property, and will be notified whenever that property changes. On the model side, you get KVO for free; simply calling a method named setMessages: will trigger the KVO change notification. You can also trigger the notification manually, and, if so desired, the KVO notification can include which indexes of the array have been added, removed, or changed.
KVO is a standardized way to do these kinds of change notifications. It's particularly important when implementing an OS X app using Cocoa Data Binding.
NSNotificationCenter is more flexible in that you can bundle any additional info with each notification.
It's important to ensure that your messages list is only updated on the main thread, and that the messages list is never modified without also posting a corresponding change notification. Your controller should also take care to ignore these notifications whenever it is not the top-most view controller or not on screen. It's not uncommon to register for change notifications in viewWillAppear: and unregister in viewWillDisappear:.
In my opinion using a delegate protocol pattern would be a much better fit for this scenario. Consider the scenario where your "api layer" needs used across many view controllers in an application. If another developer were to be introduced to your code, they would have to hunt around for notificationcenter subscriptions instead of just following a clean 'interface' like protocol.
That being said, your code will work just fine and this is a valid use of notification center. It is just my personal preference for 'cleaner' code to use a protocol based approach. Take a look around in the iOS SDK itself and you will see scenarios where Apple themselves use protocols and use notifications. I feel it is much more easy to comprehend and use the protocols than having to dig around and determine what I must listen to for a notification.
NSNotifications run the receivers code synchronously as soon as they are posted, so a new message during repopulation would join the back of that execution queue. On the whole it seems valid to me, and it keeps a reasonable degree of separation between The view controllers and the model.
Depending on the number of classes that are likely to want to listen for the same information arriving, you may want to use a delegate pattern, maybe keeping an dictionary of delegate objects, but I personally don't feel as though this scales so well, and you also have to take care of nil-ing out delegates if a page is dealloced to avoid crashes. To sum up, your approach seems good to me.

Sending an event up the UIResponder chain?

I just created a custom UIViewController with some user actions like touch. I would like to handle the user interaction in the parentObject. In other words the one that created the ViewController.
From other languages I am used to use Events that are pushed up. So my parent object would have some kind of listener on the reference of the ViewController object it can react to.
How would that type of interaction handled by Objective C?
This can be done by 1) responder chain, 2) notifications and 3) delegates.
All UI objects form the responder chain, starting from the currently focused element, then it's parent view and so on, usually until the application object. By sending action to the special First Responder object in your nib, you'll throw it down the responder chain until someone handles it. You can use this mechanism for firing events without knowing who and when will handle them. This is similar to HTML event model.
Notifications send by NSNotificationCenter can be received by any number of listeners. This is the closest analog to e.g. C# events.
Delegates is the simplest mechanism of sending event to one single object. The class declares weak property named delegate that can be assigned with any object, and a protocol that this object is supposed to implement. Many classes use this approach; the main problem is that you can't have more than one listener this way.
you should look into delegations/delegate for interactions between two viewControllers. You need to understand how it works first.
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
It sounds like you need to implement a delegate protocol, which will allow your 'child' view controller to communicate back to it's 'parent'
See http://developer.apple.com/library/ios/#documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html

Bindings AND target/action?

I currently have a color well which keeps track of a color that gets saved in the NSUserDefaults. It is bound to an NSUserDefaultsController. However, I also want to listen for changes to the color so I can update my views accordingly. Therefore, in addition to the binding, I added a target/action to the color well to my preferences controller that posts a notification with the color.
1) How safe is having both target/action and bindings? Is there a possibility that one might lag or they may be out of sync and report different values?
2) When I am getting the color in my IBAction method, should I get it from the user defaults or from the color well?
Here is my colorChanged: action:
- (IBAction)colorChanged:(id)sender
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[colorWell color] forKey:#"color"];
[notificationCenter postNotificationName:#"ColorChangedNotification" object:self userInfo:userInfo];
}
So should I be doing this:
[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:#"color"]];
or:
[colorWell color];
Thanks!
1) How safe is having both
target/action and bindings? Is there a
possibility that one might lag or they
may be out of sync and report
different values?
I think for the most part, it should be OK. The best way to tell is to test it out.
2) When I am getting the color in my IBAction method, should I get it from the user defaults or from the color well?
You should definitely, definitely get it directly from the color well. Why? There could be a lag when saving to the user defaults. Heck, the defaults could even save only once right before the application terminates, and it would still be alright. (OK, this isn't entirely true, but still) The defaults' main purpose is to persist data in between application launches, not during the lifespan of the app.
It is safe to have both target/action and bindings. If you post notifications with an NSNotificationCenter, then the notifications are delivered synchronously to the observers. (With the obvious caveat that it is not magic--if observer A sends a message to observer B when it gets the notification, observer B will not have received the notification yet. Multiple threads add further complexity.) This is called out in the documentation for NSNotificationCenter.
Reading the color directly from the color well is fast, and probably fine from an IBAction. If you're running code when the application is starting it is best to read from the user defaults because the color well's bindings might not have been updated yet.