0

I have other questions on SO about making a sandboxed app launch at login, that apparently have no solution: here and here

As you know, you have to create a helper application that will launch the main app and die.

All tutorials out there say to add this to the helper delegate:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

  // Get the path for the main app bundle from the helper bundle path.
  NSString *basePath = [[NSBundle mainBundle] bundlePath];
  NSString *path = [basePath stringByDeletingLastPathComponent];
  path = [path stringByDeletingLastPathComponent];
  path = [path stringByDeletingLastPathComponent];
  path = [path stringByDeletingLastPathComponent];

  [[NSWorkspace sharedWorkspace] launchApplication:path];
  [[NSApplication sharedApplication] terminate:self];
}

This code does exactly nothing because the main app is not launched (for the reasons you will see on my other questions) and as a bonus the helper is not killed.

I have tried to kill the helper using a variety of methods, like

[NSApp terminate:self];

and even this dramatic method

NSArray *runningApplications = [[NSWorkspace sharedWorkspace] runningApplications];
  NSString *theName;
  NSNumber *pid;
  for ( NSDictionary *applInfo in runningApplications ) {
    if ( (theName = [applInfo objectForKey:@"NSApplicationName"]) ) {
      if ( (pid = [applInfo objectForKey:@"NSApplicationProcessIdentifier"]) ) {
        //NSLog( @"Process %@ has pid:%@", theName, pid );  
        if( [theName isEqualToString:@"MyHelper"] ) {
          kill( [pid intValue], SIGKILL );
        }
      }
    }
  }

Nothing kills the helper.

As another bonus, when I launch the main app manually and it sits on the menu bar, I have the option to choose QUIT from the main app itself, so I can quit the app but the main app itself is not killable too, using the same programmatically methods.

What is going on?

I have followed @vadian instructions and it is not working. I have uploaded a proof of concept project to here. You will see that the helper loads but not the app.

Duck
  • 34,902
  • 47
  • 248
  • 470

1 Answers1

1

Your method cannot work because it's incomplete. At least you have to add these two path components to get the path to the executable of the main app (replace MyApp with the name of the executable in the MacOS folder)

path = [path stringByAddingPathComponent:@"MacOS"];
path = [path stringByAddingPathComponent:@"MyApp"];

Nevertheless the recommended and reliable way is to check if the main application is already running and to send an LaunchConfigurationArgument to the main application to indicate that the app is launched at login (and of course to use the modern URL related API of NSBundle and NSWorkspace):

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    BOOL alreadyRunning = NO, isActive = NO;
    NSArray *running = [[NSWorkspace sharedWorkspace] runningApplications];
    for (NSRunningApplication *app in running) {
        if ([[app bundleIdentifier] isEqualToString:@"com.myCompany.MyApp"]) {
            alreadyRunning = YES;
            isActive = [app isActive];
            break;
        }
    }

    if (!alreadyRunning || !isActive) {
        NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
        NSMutableArray *pathComponents = [[bundleURL pathComponents] mutableCopy];
        NSUInteger numberOfPathcomponents = [pathComponents count];
        [pathComponents removeObjectsInRange:NSMakeRange(numberOfPathcomponents - 3, 3)];
        [pathComponents addObject:@"MacOS"];
        [pathComponents addObject:@"MyApp"];
        NSURL *newURL = [NSURL fileURLWithPathComponents:pathComponents];
        NSDictionary *dict = @{NSWorkspaceLaunchConfigurationArguments: @[@"launchedAtLogin"]};
        [[NSWorkspace sharedWorkspace] launchApplicationAtURL:newURL
                                                      options:NSWorkspaceLaunchWithoutActivation
                                                configuration:dict
                                                        error:nil];
    }
    [NSApp terminate:nil];
}

Basically you don't have to quit the helper app from somewhere else. The helper app is supposed to quit itself in any case.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Are you talking about the binary? shouldn't `[pathComponents addObject:@"Contents"];` be added too before `MacOS`? – Duck Jul 23 '17 at 18:46
  • Indeed, you're right. I overlooked the first `stringByDeletingLastPathComponent`. If you delete one of those (remaining 3) you **are** in `Contents`. As you can see in my suggestion only three path components are omitted. – vadian Jul 23 '17 at 18:50
  • Unfortunately your solution is not working for me. I have uploaded a proof of concept project to [here](https://ufile.io/cnqrs) – Duck Jul 24 '17 at 11:25
  • I'm using this code in 3 different projects (also in Swift). It's supposed to work. There are many other places where the error can come from. – vadian Jul 24 '17 at 11:27
  • Please read [this tutorial](https://blog.timschroeder.net/2012/07/03/the-launch-at-login-sandbox-project/). It describes in detail how to launch a sandboxed app at login. – vadian Jul 24 '17 at 11:39
  • I have read all tutorials on the internet and for that reason I have prepared this project that shows it does not work on Sierra for an app that is sandboxed, windowless, menu only. If you ever run the project that I have prepared you will see. Anyway, thanks. I will continue researching. You will see that the main app is marked with Application is Agent on Info.plist because I don't want a dock icon. This is what is preventing the app launching. You remove that and it works. – Duck Jul 24 '17 at 11:52
  • WOOOOOOOOOOOOOOOWWWWWWW, I have finally managed to make it work. The problem was probably some kind of corruption in the project. I have created another project and transferred everything from the old one to the new, recreated the helper and now it is working! Did I mention I hate Xcode? Man, this thing is awful. No error whatsoever, no clue, simply not working! THANKS!!!!!!! – Duck Jul 24 '17 at 15:28