Apps opening Apps

gtoledo3's picture

This is helpful for me because it allows me to use apps built with QuartzBuilder or Xcode (AppKit/ofx/Cinder stuff/blah blah) as helper apps that are bundled in resources, with launch/quit able to be controlled from a primary app. You can use this premise to launch an app that's not in your bundle as well.

This is nice for scenarios where you actually have to have a secondary app running to monitor whether or not a process is working correctly in a primary app. Other times, it's just expedient to use apps together, if for no other reason.

Easy solution:

Use NSWorkspace to launch the app, either at your app start up or IB action.

Optional: Provide an IB action to trigger the app to quit via your menu (or whatever).

Step 1:

In IB, I created some new menu items - basically "start helper app", and "close helper app". I linked them to some open and stop IB actions.

Step 2:

In my code, where I'm setting up some other IB actions in the App Controller, before ApplicationDidFinishLaunching stuff, I setup this to send the signal to open my helper app.

- (IBAction) open:(id)sender
   NSBundle *mainBundle = [NSBundle mainBundle];
   NSString *helperAppPath = [[mainBundle bundlePath]
   [[NSWorkspace sharedWorkspace] launchApplication:helperAppPath];

If you just put something like that in your initialization routine, you can have the secondary app open when your app opens.

To set a menu item to close the helper app, I'm using Apple Events, because there's a little greater backwards compatibility... and my compiler is flagging some stuff that docs say is supposed to work in 10.6 even though my project isn't set anywhere for 10.5 (at least I think so...). I think one can probably use some shared workspace methods to close in +10.6, but this should work too.

Close method:

- (IBAction) stop:(id)sender
   OSStatus err;
        AppleEvent event, reply;
    const char *bundleIDString = "com.georgetoledo.mycoolapp"; //the bundle id from the app's plist
    err = AEBuildAppleEvent(kCoreEventClass, kAEQuitApplication,
                            bundleIDString, strlen(bundleIDString),
                            kAutoGenerateReturnID, kAnyTransactionID,
                            &event, NULL, "");
    if (err) return;
    err = AESendMessage(&event, &reply, kAENoReply, kAEDefaultTimeout);

I felt like this might be a helpful rundown if you do stuff like this and it just hasn't occurred to you it's possible to do this, this way.

Per one note I noticed at the macnn forum, you could also do this to launch using only Foundation, and not AppKit stuff with a few more lines of code:

NSBundle *bundle = [NSBundle bundleWithPath:@"/Applications/"]; // or whatever app
NSString *path = [bundle executablePath];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:path];
[task launch];
[task release];
task = nil;