How to use KVO to detect when an application gets active? - macos

I have the following code in a Cocoa application:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSArray* arrayAppList = [[NSWorkspace sharedWorkspace] runningApplications];
}
My intention is to use KVO to detect an application when changes its state between inactive to active.
I read that I have to use the instance method -addObserver:forKeyPath:options:context:
And then use -observeValueForKeyPath:ofObject:change:context: to respond to change notifications.
I understand that -observeValueForKeyPath is a callback method where I can write code to respond to the properties changes I am interested in.
Nevertheless, I feel confused in how I must to use the addObserver method in order to be notified when the active property of the runningApplications change. Now, I am wondering where is the place to make the registration, for now I am using -applicationDidFinishLaunching but not sure if is the right place to do it. Additionally if I use the -observeValueForKeyPath callback method, I have to implement it in the class that inherits from NSObject and is the same class where I am registering the notification?

You should call the addObserver:… method on each object in the runningApplications array (using isActive as the key path).
Starting the observing after your app finishes launching sounds about right. Time-wise, that is. As for the place, there should be a separate class dedicated to these observations. By implementing the observation code right in the app delegate you would violate the single-responsibility principle (and that means headache in the long term).
The observeValueForKeyPath:… callback should be implemented by the object that called the addObserver:… methods.

Related

How to manage windows in cocoa

I know the question is a bit generic but I guess my issue is generic as well.
I'm developing a small application in my free time and I decided to do it with Cocoa. It's nice, many things works almost automagically, but sometimes it's quite hard to understand how the framework works.
Lately I'm facing a new problem. I want to manage all the windows of the application from a single class, a front controller basically. I have a main menu and an "Import data" function. When I click it I want to show another window containing a table and call a method for updating the data. The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class? And more important, which should be the right way to do it? Should I extend the NSWindow class so that I can receive an Instance of NSWindow that can control the window containing the table (and then call the method)?
I may find several ways to overcome this issue, but I'd like to know which one is the best practice to use with cocoa.
PS: I know that there are tons of documentations files, but I need 2 lives to do everything I'd like to, so I thought I may use some help asking here :)
The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class?
These two sentences don't make sense, but I think I understand what you're getting at.
Instead of subclassing NSWindow, put your import window's controlling logic – including your NSTableViewDataSource methods – into a controller class. If the controller corresponds to a window, you can subclass NSWindowController, though you don't have to.
You can implement -importData: as an IBAction in your application delegate, then connect the menu item's selector to importData: on First Responder. That method should instantiate the import window controller and load the window from a nib.
In your import window controller's -awakeFromNib or -windowDidLoad method, call the method which updates the data.
Added:
Here's the pattern I'd suggest using in your app delegate:
#property (retain) ImportWindowController *importWC;
- (IBAction) showImportWindow:(id) sender {
if (!self.importWC)
self.importWC =
[[ImportWindowController alloc] initWithWindowNibName:#"ImportWindow"];
[self.importWC refreshData];
[self.importWC.window makeKeyAndOrderFront:sender];
}

Clarification of the exact function of NSNotificationCenter

I'm still picking up ObjC and I'm just trying to make sure I understand the concept of NSNotifications fully:
The [NSNotificationCenter defaultCenter] is a stationary object which is not the sender or the receiver. It merely routes an NSNotification, but in no way, shape, or form handles the event (by default).
Is that correct?
Theory:
Would that allow an AppDelegate to push a notification to the defaultCenter and have something further on in the responder chain / display list (e.g., UITableViewCell) pick up on the action?
Exactly. NSNotificationCenter is just the clearinghouse for the notifications. It keeps track of all the objects observing each notification, so that when a notification is posted, it can be routed to all the right observers.
And yeah, no reason why your AppDelegate can't post a notification that gets picked up by things like a UITableViewCell. NSNotifications are great for situations where an object has to send data to other objects, or tell them that something happened, and you won't know what the recipients should be until runtime.

How to Get the Current NSManagedObjectContext in a Document based Core Data / Cocoa application?

The title question arises for me when working in many areas of the application: models, controllers, getters, setters, actions, etc. I have a Core Data document-based application and I constantly need to get a reference to the current NSMangedObjectContext object.
The current scenario involves a custom controller I made to handle "simulations" (this application simulates a particular kind of mathematical model.) There is a button called "Simulate" and it is bound to an action in the simulation controller. The simulation controller needs to get information from the current document i.e. information from the NSManagedObjects in the current managed object context.
The simulation controller is a subclass of NSObjectController which has a method called managedObjectContext but when I call that method, I get nil. I'm not sure why nil is returned but I do know that the controller is not acting on behalf of any managed objects. It's controlling the simulator, which is not a model in MVC. The controller is an interface between the views, models, and the simulator.
How do I get the NSManagedObjectContext that is storing NSManagedObjects for the currently open window? The currently open window has views showing information from objects in the context, and the simulation button is in this window's toolbar.
Update:
Well... thanks to TechZen for opening my mind a little... or maybe it was just taking a break and going to a BBQ...
For this particular scenario the answer is:
Bind the managed object context in Interface Builder to my controller (the controller was created in interface builder and then I changed the class to be my subclass of NSObjectController). This setting can be found in the Bindings Inspector under Parameters and is called Managed Object Context. I set it to bind to the File's Owner and the model key path to "managedObjectContext".
Then, the message "managedObjectContext" works within my controller just like:
[self managedObjectContext];
This, however, still doesn't fully answer my question because I'd also like to get a reference to a managed object context in a class method of a subclass of NSManagedObject. So if the simulation controller then creates a new NSManagedObject by calling a class method on my subclass of NSManagedObject, I'd like that method to have a reference to the context and create the object. And I do not want to pass the context to the method... I feel like the class methods should be able to get the context... I remember seeing code like:
[[NSApp delegate] managedObjectContext];
But this didn't work for me. But if it did, this would be an excellent solution becauxe NSApp is global and returns the current NSApplication instance.
Update:
Well, after a lot of reading and Googling... I discovered that I was just totally off the mark in the design of my application. Instead of having a simulation controller receive the click of the simulation button, I created a custom NSWindowController for that window and it receives the simulation button click event (a kind of IBAction). NSWindowControllers have a reference to the NSPersistantDocument, which has a reference to NSManagedObjectContext. And this custom window controller I wrote get's that NSManagedObjectContext and passes it to the simulation controller... and the world is filled with joy. Not sure how useful this is to others, since this question and answer are now littered with noise.
If you are using a Core Data document based application, then each document will be an instance of NSPersistentDocument which will have its own NSManagedObjectContext for just that single document.
You can get the context by:
NSManagedObjectContext *context=[aPersistentDocument managedObjectContext];
Update:
With a document based app, you don't have a single, central or master managed object context because each document has it's own wholly independent Core Data stack from the store to the context. You end up with as many context has you have open documents.
This contrast with a more database like design in which the entire app has only those stores and context defined when the app was coded. In that case, you may have only one context that is used everywhere in the app. In that case, you can park the context in the app delegate and access it from anywhere.
It's bad design to have a class or instance method in a NSManagedObject subclass that finds a context because this limits the flexibility of the use of the subclass. To do so the class would have to be wired into the structure of a specific app so the subclass could only be used in a specific design where it could find the context. If you changed anything about the location of the context or the use of the subclass, you would have to write it all over again.
I think you need to back out and rethink your design and decide if you want a document based app or a more database-like app. I would recommend reading:
Document-Based Applications Overview and NSPersistentDocument Core Data Tutorial

Communicate with NSView from another class

I have an NSView thats used as a status item and I need to run this on/in it:
thingOne = NO;
[self setNeedsDisplay:YES];
but can't figure out how. I tried sending a notification (form another notification a class gets) but the notifications never received. I also tried to add a method to do this but it requires using a + symbol for it and I can't access the classes variables. How can this be done? (If it's even possible)
Thanks for any help
I was able to send the notification to the app delegate. Then I added a method to the view object to change the bool and had the notification call it.

How to use NSWindowDidExposeNotification

I am trying to update another windows when the one becomes visible. So I found the NSWindowDidExposeNotification and tried to work with it, so I wrote in my awakeFromNib:
// MyClass.m
- (void)awakeFromNib {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(mentionsWindowDidExpose:)
name:NSWindowDidExposeNotification
object:nil];
}
and implemented the method
// MyClass.h
- (void)mentionsWindowDidExpose:(id)sender;
// MyClass.m
- (void)mentionsWindowDidExpose:(id)sender {
NSLog(#"test");
}
But it never gets called which is odd. What do I do wrong here?
Generally speaking, you would set up your controller as the window's delegate in order to receive these notifications, like so:
// MyClass.m
- (void)awakeFromNib {
// note: this step can also be done in IB by dragging a connection
// from the window's "delegate" property to your `MyClass` object
[window setDelegate:self];
}
- (void)windowDidExpose:(NSNotification *)notification {
NSLog(#"test");
}
Although, after reading here and here, windowDidExpose may not be your best bet. I would recommend trying the windowDidBecomeKey delegate method instead. That one is posted whenever your window gains "focus" (starts responding to user input) which may be the right time to show your second window.
Update: (in response to comments)
Apple's documentation (quoted below) indicates that NSWindowDidExposeNotification is only valid for nonretained windows, which, according to the posts that I linked above, are quite uncommon.
NSWindowDidExposeNotification
Posted whenever a portion of a nonretained NSWindow object is exposed, whether by being ordered in front of other windows or by other windows being removed from in front of it.
The notification object is the NSWindow object that has been exposed. The userInfo dictionary contains ... the rectangle that has been exposed.
On a higher level, NSNotification objects are simply packages of data that get passed around between Cocoa classes and NSNotificationCenter objects. NSNotificationCenter objects are controllers that manage these packages of data and send them out to observers as required. There is usually no need to trap notifications directly. You can simply use KVC/KVO or pre-defined delegates in your classes and Cocoa handles all of the dirty details behind the scenes.
See Notification Programming Topics and Key Value Coding Programming Guide if you want to know more.

Resources