Is it possible to monitor other application using Cocoa on Mac? - objective-c

For example, get the notification that another Application is becoming Active on the screen, or resign active state.

Sure. In your app delegate class, you can use NSWorkspace to get notified when an app becomes active (NSWorkspaceDidActivateApplicationNotification) or resigns active (NSWorkspaceDidDeactivateApplicationNotification). See the documentation on NSWorkspace for more info.
In your controller class, you'd do something like this:
- (id)init {
if ((self = [super init])) {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDidActivate:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)appDidActivate:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSLog(#"userInfo == %#", userInfo);
}
The key points are basically that you need to register to receive the notifications like shown in -init. You'd repeat the code to add another observer for each additional notification name that you want (e.g NSWorkspaceDidDeactivateApplicationNotification).
Another important thing to remember is to remove yourself as an observer in -dealloc (or elsewhere), so that NSWorkspace doesn't try to notify your controller object after it's been released+dealloc'd (and would no longer be valid).
In the specified -appDidActivate: method, do whatever you need to with the info about the app in question.

If you want something simpler than distributed objects, you could use distributed notifications from the distributed notification center. However, these are not posted unless you built the application. For monitoring when applications start or quit, you can use NSWorkspace and its notification center (suggested by NSGod)

Related

Register a function to be called just before program is quit

So I need to register a function on OSX (using Xcode / objective C) that will register a particular function to be called whenever the program terminates.
I ran into this but i guess that's for iOS and not for OSX.
I replaced UI with NS and gave it a shot, but it did not work.
NSApplication *app = [NSApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:BeCalledOnExit object:app];
but that is not compiling. it says something on name: being an undeclared identifier when it is clearly on the .h & .m file before the function.
i ran into another chap who used this:
-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) sender{
return TRUE
}
but it doesnt work for me as my app is a complete status bar app.
Basically i'm creating some temp files during my app and just before quitting, i want to make sure that my app is clearing out these files. I am putting it in /tmp/.. Woudn't want too much space to be taken.
i would really like to have a solution like in gcc/python,
atexit(functionName);
Implement - (void)applicationWillTerminate:(NSNotification *)notification on your AppDelegate and it will be called just before the application will terminate
So the Implementation of your AppDelegate could look something like:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSLog(#"I FINISHED LAUNCHING!");
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
NSLog(#"I WILL TERMINATE NOW!!!");
}
#end
Your App Delegate should implement the - (void)applicationWillTerminate:(NSNotification *)notification to get the notification
(OR)
You can implement applicationShouldTerminate and perform clean of your files and return appropriate NSApplicationTerminateReply if your clean up fails
If you want to use a notification, you should be observing NSApplicationWillTerminateNotification.
NSApplication *app = [NSApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name: NSApplicationWillTerminateNotification object:app];
/*A notification named NSApplicationWillTerminateNotification.*/
- (void)applicationWillTerminate:(NSNotification *)notification
{
}

How does one detect when a certain application opens? [duplicate]

This question already has an answer here:
How could I get notifications in my app when a different app starts on Mac OS X?
(1 answer)
Closed 10 years ago.
Is there a way, in either Applescript or Objective-C, to detect when a certain application opens? My goal is to add a feature to an application that I'm working on to show a message whenever "QuickTime Player" opens, but I haven't found a anything in the Apple developer documents that shows how to do anything like this.
This is pretty simple with Objective-C. Here's the code:
Register for the proper notifications from NSWorkspace:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//Fetch the notification center from the workspace
NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver:self selector:#selector(newApplicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[center addObserver:self selector:#selector(newApplicationWillLaunch:) name:NSWorkspaceWillLaunchApplicationNotification object:nil];
}
Then, add your selectors for the notification. The userInfo dictionary of the notification will hold everything you need to know:
-(void)newApplicationDidLaunch:(NSNotification*)notification {
NSDictionary* userInfo = notification.userInfo;
//Do what you want here after application launch.
}
-(void)newApplicationWillLaunch:(NSNotification*)notification {
NSDictionary* userInfo = notification.userInfo;
//Do what you want here to prepare for application launch.
}
Hope that helps.

Add observer for a process in Cocoa

I'm developing kind of a plugin for iTunes.
A lot of user have requested, that they would like to start the plugin if they start iTunes, which of course makes sense. However, I'm not sure how to do this.
I thought about a helper app, which is probably the only way.
The only thing that bothers me is how to get the notification.
Of course I could consistently check if iTunes is running, but I'm not sure if that's the right way to do it.
I would rather add my app as an observer of that process.
Is that possible?
If not, how does Activity Monitor do it?
SOLUTION
Thanks to Daij-Djan! I got it working like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesLaunched:)
name:NSWorkspaceDidLaunchApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesTerminated:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
-(void) iTunesLaunched:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"start");
}
-(void) iTunesTerminated:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"terminate");
}
register for NSWorkspace notifications:
NSWorkspaceDidLaunchApplicationNotification
NSWorkspaceDidTerminateApplicationNotification
see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html
there is also the possibility to KVO the runningApplications property
btw cocoatech has a nice NTRunningAppManager class that does just that

How to call a selector in a different class?

I'm trying to use this code but Xcode returns an error because the method I'm trying to call in the selector:#selector() is in another class. Thanks for your help!
AppDelegate.m:
-(void)applicationDidBecomeActive:(UIApplication *)application{
[..]
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMethodHere) name:UIApplicationDidBecomeActiveNotification object:nil];
}
MainViewController.m:
-(void)myMethodHere{
[..]
}
The problem is that you use
addObserver:self
which means that it looks for the function in the current class. Instead do something like
addObserver:instanceOfOtherClass
Update
Add the call to the init method of MainViewController
// MainViewController.m
- (id)init;
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(someMethod) name:UIApplicationDidBecomeActiveNotification object:nil];
}
return self;
}
Make sure to remove yourself in dealloc
- (void)dealloc;
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
By doing it this way from the very moment the object comes in to existence it is ready to receive notifications and then when it is being deallocated it will safely remove itself.
A good pattern to follow is to make the class that is doing the observing responsible for registering for notifications. This keeps encapsulation well and removes some risk of sending notification to deallocated instances.
Rationale
You need to balance your calls for registering for notifications and unregistering for notifications otherwise a message may be called on a deallocated object which could be hard to track down.
If I have a class that needs to be notified of an event the likely hood is I will register for the notifications in the init method and then unregister for the notifications in the dealloc (init and dealloc are just examples of times I often do this, not necessarily the best place in every example, do what makes sense in your case).
The issue is your use of
addObserver:self
The observer needs to be an instance class that contains the method you want to call, so create that first and then add the notification. Something like.
-(void)applicationDidBecomeActive:(UIApplication *)application{
[..]
SomeClass *newObject = [[SomeClass alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:newObject selector:#selector(someMethodContainedInSomeclass) name:UIApplicationDidBecomeActiveNotification object:nil];
}

NSNotificationCenter Scope definition

So I'm new to NSNotifications, I am wondering what the scope is. I.e. If I have an Application Delegate Class, and it is the receiver of a notification:
-(id)init
{
[ super init];
if (!self) return nil;
// Add to our notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveUpdateRequest:)
name:#"RequestStatusUpdate"
object:nil];
return self;
}
And has this method run on receive:
- (void) receiveUpdateRequest:(NSNotification *) notification
{
// Check the Notification Name
if ([[notification name] isEqualToString:#"RequestStatusUpdate"]){
NSLog (#"Recieved Update Status!");
}
else {
NSLog(#"Recieved Notification: %#",[notification name]);
}
}
Can I post a notification like so:
[[NSNotificationCenter defaultCenter] postNotificationName:#"RequestStatusUpdate" object:self];
From another object instance any where in my App?
Even for instance an object that instantiated by virtue of a NIB being loaded:
summaryWindow = [[SummaryWindowController alloc] initWithWindowNibName:#"SummaryWindow" owner:globalStatusController];
Do I have to have anything else configured in my summaryWindow Class to be able to call the postNotificationName method.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application, I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
I have tested both [NSThread currentThread] and the default Notification center and it does look like I'm in the thread and the same notification center ( which I think is always global). I am only looking into the thread thing as its come up on a few other threads.
2011-08-22 20:57:11.452 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
2011-08-22 20:57:20.366 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
Wow that was lame, I just found [[NSNotificationCenter defaultCenter] removeObserver:self]; in some earlier code. I had it in dealloc but some how managed to miss it in another NSTask method I was working on.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application
Yes.
I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
That's because you're registering in init. I'm betting this is Mac, and on Mac the application delegate is almost always instantiated from a nib file. You need to do this work in awakeFromNib.
Note that you generally do not need to check the notification's name. It's generally best to have a different method for each notification callback. You should also always create a string constant for your notification names. It's way too easy to mis-type them.