Re: Quit helper app when main app terminates
Re: Quit helper app when main app terminates
- Subject: Re: Quit helper app when main app terminates
- From: Jerry Krinock <email@hidden>
- Date: Thu, 12 Jul 2012 13:11:24 -0700
Thanks for all the ideas.
I thought maybe someone would suggest opening up some snazzy inter application communication channel and making the helper exit when it broke (indicating that the main app had terminated). But since that was not offered, I used a combination of the suggestions and some code I had lying around. It's all done in the helper…
(1) Register for NSWorkspaceDidTerminateApplicationNotification
(2) In case the main app terminates before that notification gets going, during launch, check that the main app is still running.
This might even work in a sandboxed helper, although, Yay! I'm not sandboxed in this particular case.
On 2012 Jul 12, at 12:50, Sean McBride wrote:
> Although rare, it is possible to have more than one app with the same bundle id running at the same time.
Yes, in case someone launches two instances of my app and quits one, I'd have two helpers running. But I think that's OK because all the helper does is listen for and forward myapp:// url events, and the system will only send such url event to one of them.
Jerry
The following code assumes that the MyApp-Helper.app is actually a .app with a run loop, etc., and it is packaged in the main app in Contents/<Something>/
@implementation MyHelperAppDelegate
/* Other methods go here */
- (NSString*)mainAppBundleIdentifier {
NSString* bundlePath = [[NSBundle mainBundle] bundlePath] ;
// bundlePath = /Applications/MyApp.app/Contents/Helpers/MyApp-Helper.app"
bundlePath = [bundlePath stringByDeletingLastPathComponent] ;
// bundlePath = /Applications/MyApp.app/Contents/Helpers/"
bundlePath = [bundlePath stringByDeletingLastPathComponent] ;
// bundlePath = /Applications/MyApp.app/Contents/"
bundlePath = [bundlePath stringByDeletingLastPathComponent] ;
// bundlePath = /Applications/MyApp.app/"
NSBundle* bundle = [NSBundle bundleWithPath:bundlePath] ;
return [bundle bundleIdentifier] ;
}
- (void)handleAppQuit:(NSNotification*)appTerminateNotification {
NSString* osx_10_5_quitAppBundleIdentifier = [[appTerminateNotification userInfo] objectForKey:@"NSApplicationBundleIdentifier"] ;
NSString* osx_10_6_quitAppBundleIdentifier = [[[appTerminateNotification userInfo] objectForKey:NSWorkspaceApplicationKey] bundleIdentifier] ;
NSString *mainAppBundleIdentifier = [self mainAppBundleIdentifier] ;
if (
[osx_10_6_quitAppBundleIdentifier isEqualToString:mainAppBundleIdentifier]
||
[osx_10_5_quitAppBundleIdentifier isEqualToString:mainAppBundleIdentifier]
) {
[NSApp terminate:self] ;
}
}
- (pid_t)pidOfThisUsersAppWithBundleIdentifier:(NSString*)bundleIdentifier {
pid_t pid = 0 ; // not found
if (bundleIdentifier) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
// Running the main run loop is necessary for -runningApplications to
// update. The next line is actually necessary in tools which may be lacking
// a running run loop, and it actually works.
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]] ;
NSArray* runningApps = [[NSWorkspace sharedWorkspace] runningApplications] ;
for (NSRunningApplication* runningApp in runningApps) {
if ([[runningApp bundleIdentifier] isEqualToString:bundleIdentifier]) {
pid = [runningApp processIdentifier] ;
break ;
}
}
#else
NSArray* appDicts = [[NSWorkspace sharedWorkspace] launchedApplications] ;
// Note that the above method returns only applications launched by the
// current user, not other users. (Not documented, determined by experiment
// in Mac OS 10.5.5). Also it returns only "applications", defined as
// "things which can appear in the Dock that are not documents and are launched
// by the Finder or Dock"
// (See documentation of ProcessSerialNumber).
for (NSDictionary* appDict in [appDicts objectEnumerator]) {
if ([[appDict objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:bundleIdentifier]) {
pid = [[appDict objectForKey:@"NSApplicationProcessIdentifier"] unsignedLongValue] ;
break ;
}
}
#endif
}
return pid ;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Begin watching for main app to quit
NSNotificationCenter* notificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter] ;
[notificationCenter addObserver:self
selector:@selector(handleAppQuit:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil] ;
// Tested in Mac OS X 10.7: That notification is received whether the application
// quits normally, crashes, or is terminated by a unix signal.
// Make sure main app did not terminate before we got here
pid_t pid = [self pidOfThisUsersAppWithBundleIdentifier:[self mainAppBundleIdentifier]] ;
if (pid == 0) {
// Main app must have terminated before we finished launching
[NSApp terminate:self] ;
}
// Other code goes here
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden