Question

In OS X how can I automatically re-start my app after pulling down an updated version?

I've looked around some and launchd seems to be the likely way to do this, but I can't seem to get my head around it. I can't seem to find any good resources talking about this specifically either.

I could also create a script or separate process to do this, but that seems clunky at best, I'm expecting there is a better solution out there.

Was it helpful?

Solution

Separate process is good solution, look at Sparkle framework (most of apps use it for autoupdating), they also use for this standalone app - https://github.com/andymatuschak/Sparkle/blob/master/relaunch.m

OTHER TIPS

Just expanding on Julia's answer, since I had to restart my App for another reason than updating, I looked into how Sparkle does it -

Latest version(as of 11/2011) of Sparkle has a project target called finish_installation.app that is included in the Resources dir of the Sparkle framework. Sparkle, running as part of the host App, copies finish_application to the application_support directory and uses launched to run its binary executable like this, passing in the host process id and relaunch path:

NSString *relaunchToolPath = [NSString stringWithFormat:@"%@/finish_installation.app/Contents/MacOS/finish_installation", tempDir];
[NSTask launchedTaskWithLaunchPath: relaunchToolPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], tempDir, relaunch ? @"1" : @"0", nil]];
[NSApp terminate:self];

Seems like with this function, when the parent process quits(?), finish_application's parent becomes launched.

finish_installation waits for the passed in process id to disappear, also has an initial check to see if it's parent is launched (pid=1)

if(getppid() == 1)...
if (GetProcessForPID(parentprocessid, &psn) == procNotFound)...

then launches app with:

[[NSWorkspace sharedWorkspace] openFile: appPath];

Last interesting tidbit: If installation takes a long time, finish_installation changes itself to a foreground process so the user can see that some app is running:

ProcessSerialNumber     psn = { 0, kCurrentProcess };
TransformProcessType( &psn, kProcessTransformToForegroundApplication );

Source Blog

- (void) restartOurselves
    {
       //$N = argv[N]
       NSString *killArg1AndOpenArg2Script = @"kill -9 $1 \n open \"$2\"";

       //NSTask needs its arguments to be strings
       NSString *ourPID = [NSString stringWithFormat:@"%d",
                      [[NSProcessInfo processInfo] processIdentifier]];

       //this will be the path to the .app bundle,
       //not the executable inside it; exactly what `open` wants
       NSString * pathToUs = [[NSBundle mainBundle] bundlePath];

       NSArray *shArgs = [NSArray arrayWithObjects:@"-c", // -c tells sh to execute the next argument, passing it the remaining arguments.
                    killArg1AndOpenArg2Script,
                    @"", //$0 path to script (ignored)
                    ourPID, //$1 in restartScript
                    pathToUs, //$2 in the restartScript
                    nil];
       NSTask *restartTask = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:shArgs];
       [restartTask waitUntilExit]; //wait for killArg1AndOpenArg2Script to finish
       NSLog(@"*** ERROR: %@ should have been terminated, but we are still running", pathToUs);
       assert(!"We should not be running!");
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top