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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to