I'm new to Objective-C and programming in general but I'm beginning to grasp the syntax and have a mostly working application however I'm struggling with one part. I'd like to be able to use the application I'm writing to monitor the activity of another application, namely wether it's open or not.
Ideally when a user clicks a button it will launch remote desktop client and then monitor when remote desktop client closes. I want to know when it closes so that I can either bring my application to the forefront or to restart the computer. Mostly my problems revolve around watching for when remote desktop client closes. Here's what I was thinking of trying:
do {
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.microsoft.rdc"];
} while ([apps count] >= 1);
The problem with the approach you've posted is that that while loop will block the main thread, preventing your application from doing anything else. You could run that on a background thread to prevent that problem, but that's probably not the best approach.
Instead, take a look at the NSWorkspace class's notifications. One of them is NSWorkspaceDidTerminateApplicationNotification. You should be able to do something like this:
// Put this part in your app delegate's applicationDidFinishLaunching: method, or some other appropriate place
NSNotificationCenter *nc = [[NSWorkspace sharedWorkspace] notificationCenter];
[nc addObserver:self selector#selector(anotherAppDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
- (void)anotherAppDidTerminate:(NSNotification *)notification
{
NSRunningApplication *app = [[notification userInfo] objectForKey:NSWorkspaceApplicationKey];
if ([app.bundleIdentifier isEqualToString:#"com.microsoft.rdc"]) {
// RDC closed, so do whatever it is you want to here
}
}
Related
Coming from a .NET background I'm used to events getting fired so trapping a lost focus event is easy but I'm not sure how to do this in Obj-C. Basically I want my app to know when another application has gotten focus and it no longer has it so it can perform some actions.
Can you please tell me how I can implement this kind of functionality in Obj-C for an OSX app?
Have a look at the NSWindow notifications. Specifically, you're interested in NSWindowDidBecomeKeyNotification and NSWindowDidResignKeyNotification. You can also create a delegate for the window and implement its windowDidBecomeKey: and windowDidResignKey: methods, as noted in the NSWindowDelegate protocol documentation.
Or, if you just wanted to know when the application (not a window) has gained focus, you can subscribe to the NSApplicationDidBecomeActiveNotification. Likewise, NSApplicationDidResignActiveNotification will notify you when your app loses focus. These notifications are discussed more here. You can also implement applicationWillBecomeActive: and applicationWillResignActive: in the application delegate.
It's unclear if you want notification of a single window losing focus or notification of your entire app losing focus. My answer here provides notification for the entire application losing focus. (See mipadi's answer if you just want to know when one of your app's window loses focus.)
Observe the appropriate notification:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDeactivated:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Then add the handler method:
-(void) appDeactivated:(NSNotification *)notification
{
NSRunningApplication* app = [notification.userInfo objectForKey:#"NSWorkspaceApplicationKey"];
if (app == [NSRunningApplication currentApplication]) {
// your cleanup code here
}
}
For an iCloud plugin I'm writing, I subscribe my iCloud manager class to these iCloud NSMetaDataQuery observers:
// Add a predicate for finding the documents
NSString* filePattern = [NSString stringWithFormat:#"*.%#", #"*"];
self.metadataQuery = [[NSMetadataQuery alloc] init];
// Before starting to query, it is required to set the search scope.
arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// It is also required to set a search predicate.
[self.metadataQuery setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, filePattern]];
// Listen for the second phase live-updating
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidReceiveNotification:) name:NSMetadataQueryDidUpdateNotification object:nil];
// Listen for the first phase gathering
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryIsDoneGathering:) name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[self.metadataQuery startQuery];
The problem is that none of these selectors are actually ever called back, not even once, and I especially need the NSMetaDataQueryDidUpdateNotification to track upload/download progress of files in the cloud.
An odd thing is that I had this working the other day, but somehow it just stopped working and I've starred myself blind in trying to figure out what the problem actually is. By subscriping to the NSMetadataQueryDidStartGatheringNotification I can see that it does start, but it's like it never finishes. It is quite weird.
I was wondering if anyone have any clue at all as what to be wrong with the above code? Or where else I can look for the problem.
Thank you for your time :)
Make sure you start the NSMetadataQuery in the main thread. Back then this requirement was not documented.
This was quite the secret to dig up. I don't know if you've given up by now, but at long last, I might be able to help.
It turns out that for some (all?) C++ app configurations, there is a message pump that doesn't run automatically. In my app, I finally started getting my expected callbacks after placing a loop like this following my [m_query startQuery] call:
// make it look syncronous for now
while( m_state != finished )
{
Check_For_Interruption();
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeslice]]; //<--**this line is the key**
}
where the callbacks are setup to correctly update the m_state variable.
My example just blocks, using it's own thread timeslice to run the loop (unless interrupted by a timeout being reached), but this could also be set up in an asynchronous way.
Another thing to note for those who go overboard on legacy support:
This method did cause the app to start leaking mach ports on OS X 10.9 and older. Haven't seen any problems on anything newer than that, though.
I want to create a background application that keep track of the current active application (active window). I manage to get the active application by using GetFrontProcess but now I need to keep track of it so I can know when it changes.
I thought of two ways
1. Setting a Timmer and recheck the variable each time the timer triggers (Should I create a thread?)
2. Creating a dedicated thread, check the variable in a loop and sleep for a few Milliseconds after every iteration to avoid overhad.
Efficiency is important to me as this should be a non disturbing background thread.
Do you think those methods are right? Any efficiency considerations? Would love to hear about different ideas that are more efficient.
Thanks for your help
Gil
You could observe the NSWorkspace notification NSWorkspaceDidActivateApplicationNotification.
eg:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appActivated:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
and have a method:
-(void)appActivated:(NSNotification*) notification {
NSRunningApplication* currentApp = [notification.userInfo valueForKey:NSWorkspaceApplicationKey];
....
}
I am using Cordova 2.1.0 for IOS app development. Since, I am new to app development, I have a very basic question.
I am using applicationDidEnterBackground method to handle app control when app enters background. But i want to understand the utility of UIApplicationDidEnterBackgroundNotification which is sent when the app is entering the background. In what way can i use this notification and other notifications(like UIApplicationWillEnterForegroundNotification, etc.) sent by the system. What is the USP of these notifications.
According to the documentation, the method applicationDidEnterBackground: tells the UIApplication's delegate that the application is now in the background. In Cocoa, many delegate messages have corresponding UINotifications that are also sent. This is no exception.
According to the documentation:
The application also posts a UIApplicationDidEnterBackgroundNotification notification around the same time it calls this method to give interested objects a chance to respond to the transition.
Therefore, if there are objects in your object graph that need to respond to the state transition, they can observe this notification. I'm not sure there's really an unstated purpose beyond allowing all objects in the graph to respond to application state transition. I suppose if you had a long-running task to perform somewhere down the object hierarchy when the application transitions to background task you could use beginBackgroundTaskWithExpirationHandler: in a manner similar to what you do in the applicationDidEnterBackground.
EDIT:
// example, save NSArray *_myArray to disk when app enters background
// this is contrived, and untested, just meant to show how you can
// observe the UIApplicationDidEnterBackgroundNotification and save state
// in an arbitrary point in the object graph. (as opposed, or in addition to, the
// application's delegate.
// long-running tasks, e.g. web service connections, etc. will need to
// get a background task identifier from the UIApplication and manage that.
__block id enteredBackground = nil;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
enteredBackground = [center addObserverForName:UIApplicationDidEnterBackgroundNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[_myArray writeToFile:#"/path/to/you/file" atomically:YES];
}];
I would like to start my OSX application when iTunes loads, without having a background process to monitor when iTunes launches. The last.fm client seems to do this; I can find no background process when iTunes is closed, but as soon as it starts the last.fm app opens right along with it. Perhaps it is using some kind of iTunes plugin that can start another process?
It seems to be fairly trivial to do this with a background process, but I'd like to do it without one so my program isn't using system resources.
One option with a background process is to use NSWorkspace's notification center, such as:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:#selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil]
However, this obviously requires a background process. Another option I found was to use ProcessNotif, something like this:
ProcessNotif *x = [[ProcessNotif new] autorelease];
[x setProcessName: #"iTunes"];
[x setTarget: self];
[x setAction: #selector(doStuff)];
[x start];
This is probably even less ideal than the NSWorkspace method, and it too requires a background process.
So, is there some way to launch from iTunes when it launches, no background process required?
Thanks!
The last.fm client achieves that by installing an iTunes plugin. This plugin gets loaded when iTunes starts and then has a chance to start the last.fm app. To create a plugin you need the iTunes PlugIn SDK available here.