0

My issue is I have a child .app that I'd like to run after injecting some parameters. What want to do is run the app as the parent app (launching it sync and propagate focus/activation events to the child app).

The goal for me is to create an 'parent app' that launches another app, for example OtherApp.app. It should appear as if 'parent app' is OtherApp.app (i.e. not show up as a seperate application in the dock but the windows of OtherApp.app should be contained by 'parent app'). The reason I want to do this is so I can pass some initialization variables to OtherApp.app without modifying the .app itself.


Approaches I have taken

  1. First approach is the simplest. Simply using system(@"VAR=VALUE /Applications/OtherApp.app"). However the issue with this is that the 'parent app' will instantly exit and OtherApp.app will open as a seperate application in the Dock.

  2. Second approach: I've tried to do is use NSWorkspace with NSRunningApplication however that is not synchronous, the issue with this is that the 'parent app' will again instantly die:

    #import <Cocoa/Cocoa.h>
    
    int main(int argc, const char * argv[]) {
        NSRunningApplication* childApp = [[NSWorkspace sharedWorkspace]
                                          openURL:[NSURL fileURLWithPath:@"/Applications/OtherApp.app"]
                                          options:NSWorkspaceLaunchDefault|NSWorkspaceLaunchWithoutAddingToRecents
                                          configuration:@{
                                              NSWorkspaceLaunchConfigurationEnvironment: @{
                                                      @"VAR": @"VALUE"
                                              }
                                          } error:NULL];
    }
    
  3. Third approach is using Launch Services. However this is what my question is asking— I can't find any undeprecated API that lets me pass environment variables or "Launch Services Keys" (e.g. LSUIElement) nor can I find a way that lets me pass environment variables. This also instantly exits (I'm not to familiar with Launch Services' internals, perhaps someone can enlighten me?)

    #import <Cocoa/Cocoa.h>
    
    int main(int argc, const char * argv[]) {
        LSLaunchURLSpec launchSpec;
        launchSpec.appURL = CFBridgingRetain([NSURL fileURLWithPath:@"/Applications/OtherApp.app"]);
        launchSpec.asyncRefCon = NULL;
        launchSpec.launchFlags = kLSLaunchDefaults;
        launchSpec.passThruParams = NULL;
        // Where can I specify environment vars or args?
        return LSOpenFromURLSpec(&launchSpec, NULL);
    }
    

Possible solutions

  1. Create an NSApplication that communicates with OtherApp.app so 'parent app' doesn't exit immediately. Problems here are that, again, now they are two apps in the dock and also keeping focus in sync seems like it would be a more complex task.
  2. Figure out how to pass environment variables to LS (Launch Services) APIs along with somehow being able to control the focus of the spawned app.
  3. Somehow access the bundle and dynamically load the NSApplicationMain of the OtherApp.app though I typically can't use NSBundle with an executable (it throws an error saying so).

Right now #2 feels like the best bet though any assistance on alternative solutions would be greatly appreciated.

Downgoat
  • 13,771
  • 5
  • 46
  • 69
  • You can't really do what you want. Can the parent app just be a background-only or `LSUIElement` program so that it never has an GUI presence itself? It won't have a Dock icon or menu bar or (if you don't create them) windows. – Ken Thomases Oct 07 '18 at 22:57
  • @KenThomases I basically want a dock item that launches the app with the environment variables. So setting LSUIElement on the parent app itself won't work – Downgoat Oct 07 '18 at 23:54
  • Why do you need/want the parent app to stay around? The reason why your parent keeps exiting is because it has finished, you could prevent this by giving it something to do (which could be as little as waiting for its child to die) but why would you? What you appear to be describing is what shell scripts are often used for - gather some info, launch a program - and those scripts just then terminate. Are you trying for aesthetic reasons to have a "keep in dock" icon for the parent which gets replaced by the dynamic icon for the running child, and then restored when the child dies? – CRD Oct 08 '18 at 06:04
  • @CRD yeah, basically. I want a dock icon to bootstrap the app with the env vars. I can disguise a shell script with the icon of the app I want to start but then I still have two things in my dock, the pinned bootstrapper/parent app, and the child itself. – Downgoat Oct 08 '18 at 06:12
  • @Downgoat - I suspect doing exactly what you want is either very hard or impossible and probably not worth it even for the challenge. If you can't live with a "launcher" version of the icon in the dock, you could try: change the launchers dock icon when it launches, sleep the launcher until the child dies, quit the launcher. This will keep just one copy of the target app's icon in the dock at any time, but its position will change when running. But what to change the launcher's icon to? Be creative! The launcher could even do something useful while it waits the death of its child. Have fun! – CRD Oct 08 '18 at 11:11

0 Answers0