title: macOS Swift: How to properly add application as Login Item
body: <p>I have spent about one day (maybe a little more) on trying to add my application to Login Item in the order it starts up at macOS launch (user login). </p>
  
  <ol>
  <li>The first approach was the newest one; I check this tutorial on youtube: 
  <a href="https://www.youtube.com/watch?v=2mmWEHUgEBo&amp;t=660s" rel="noreferrer">https://www.youtube.com/watch?v=2mmWEHUgEBo&amp;t=660s</a></li>
  </ol>
  
  <p>So following this steps, I have done: </p>
  
  <ol>
  <li>Add new project inside my main project that I have named  Launcher </li>
  <li><p>I am using Automatic Signing (as version of my Xcode) is different 
  <a href="https://i.stack.imgur.com/aV1g5.png" rel="noreferrer"><img src="https://i.stack.imgur.com/aV1g5.png" alt="enter image description here"></a></p></li>
  <li><p>In Project Settings > Capabilities I toggled App Sandbox to ON. </p></li>
  <li><p>In Build Phases I have added this: 
  <a href="https://i.stack.imgur.com/wwzGt.png" rel="noreferrer"><img src="https://i.stack.imgur.com/wwzGt.png" alt="enter image description here"></a></p></li>
  <li><p>My Launcher has Skip Install = YES
  <a href="https://i.stack.imgur.com/9Cg3y.png" rel="noreferrer"><img src="https://i.stack.imgur.com/9Cg3y.png" alt="enter image description here"></a></p></li>
  <li><p>Code in my Launcher app looks like this (I have even previously use Swift to do the same) </p>
  
  <pre><code>  - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
  {
      // Insert code here to initialize your application
  
      NSArray *pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents];
      pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 4)];
      NSString *path = [NSString pathWithComponents:pathComponents];
      [[NSWorkspace sharedWorkspace] launchApplication:path];
      [NSApp terminate:nil];
  } 
  </code></pre></li>
  <li><p>Finally, I have magic code in Main App to enable app as Login Item </p></li>
  </ol>
  
  <blockquote>
  <pre><code>  if(!SMLoginItemSetEnabled("click.remotely.Remotely-Click-Server-Launcher"
   as CFString, Bool(checkboxButton.state as NSNumber) ) ) {
              let alert: NSAlert = NSAlert()
              alert.messageText = "Remotely.Click Server - Error";
              alert.informativeText = "Application couldn't be added as 
          Login Item to macOS System Preferences &gt; Users &amp; Groups.";
              alert.alertStyle = NSAlertStyle.warning;
              alert.addButton(withTitle:"OK");
              alert.runModal();
     }
  </code></pre>
  </blockquote>
  
  <ol start="8">
  <li>I have made Archive, and then have different options to Export:</li>
  </ol>
  
  <p><a href="https://i.stack.imgur.com/GibPD.png" rel="noreferrer"><img src="https://i.stack.imgur.com/GibPD.png" alt="enter image description here"></a> </p>
  
  <p>I couldn't decide which one to choose, so I tried all of them. 
  "Save for Mac App Store Deployment" - made Installation package that has installed in /Applications/ directory but the app never runs. 
  "Developer-Id signed," "Development-signed" , "macOS App" all makes file in a directory that I exported to Applications directory, but no one works. </p>
  
  <ol start="9">
  <li><p>When I click the checkbox button, I could see some window blinking for a while on the screen (Launcher program). When I log out and log in the same window blinking effect appears but Launcher didn't start the Main application. When I click checkbox button again (and turn off Login Item) this blinking effect on user login (system startup) doesn't happen again. So it seems that this addition of Launcher program as Login Item works, but this Launcher couldn't start the Main app. Moreover when I go to /Applications/Main.app/Contents/Library/LoginItems/Launcher.app and click it manually then Launcher app launch Main application correctly (so the path was correct). </p></li>
  <li><p>So what's going wrong? </p></li>
  </ol>
  
  <p>Then I consider implementation of deprecated approach using 
  <strong><code>kLSSharedFileListSessionLoginItems</code></strong></p>
  
  <p>I have thought it must work it just add something in System Preferences this
   window below. </p>
  
  <p><a href="https://i.stack.imgur.com/fR1Nv.png" rel="noreferrer"><img src="https://i.stack.imgur.com/fR1Nv.png" alt="enter image description here"></a></p>
  
  <p>But it also could go wrong! </p>
  
  <ol>
  <li><p>I have chosen implementation in Swift (all examples/tutorials I have found was in Objective-C) So I have written something like this: </p>
  
  <pre><code> class LoginItemsList : NSObject {
  
  let loginItemsList : LSSharedFileList = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue();
  
  
  
  func addLoginItem(_ path: CFURL) -&gt; Bool {
  
      if(getLoginItem(path) != nil) {
          print("Login Item has already been added to the list."); 
          return true;
      }
  
      var path : CFURL = CFURLCreateWithString(nil, "file:///Applications/Safari.app" as CFString, nil);
      print("Path adding to Login Item list is: ", path);
  
      // add new Login Item at the end of Login Items list
      if let loginItem = LSSharedFileListInsertItemURL(loginItemsList,
                                                        getLastLoginItemInList(),
                                                        nil, nil,
                                                        path,
                                                        nil, nil) {
          print("Added login item is: ", loginItem);
          return true;
      }
  
      return false;
  }
  
  
  func removeLoginItem(_ path: CFURL) -&gt; Bool {
  
      // remove Login Item from the Login Items list 
      if let oldLoginItem = getLoginItem(path) {
          print("Old login item is: ", oldLoginItem);
          if(LSSharedFileListItemRemove(loginItemsList, oldLoginItem) == noErr) {
              return true;
          }
          return false;
      }
      print("Login Item for given path not found in the list."); 
      return true;
  }
  
  
  func getLoginItem(_ path : CFURL) -&gt; LSSharedFileListItem! {
  
      var path : CFURL = CFURLCreateWithString(nil, "file:///Applications/Safari.app" as CFString, nil);
  
  
      // Copy all login items in the list
      let loginItems : NSArray = LSSharedFileListCopySnapshot(loginItemsList, nil).takeRetainedValue();
  
      var foundLoginItem : LSSharedFileListItem?;
      var nextItemUrl : Unmanaged&lt;CFURL&gt;?;
  
      // Iterate through login items to find one for given path
      print("App URL: ", path);
      for var i in (0..&lt;loginItems.count)  // CFArrayGetCount(loginItems)
      {
  
          var nextLoginItem : LSSharedFileListItem = loginItems.object(at: i) as! LSSharedFileListItem; // CFArrayGetValueAtIndex(loginItems, i).;
  
  
          if(LSSharedFileListItemResolve(nextLoginItem, 0, &amp;nextItemUrl, nil) == noErr) {
  
  
  
              print("Next login item URL: ", nextItemUrl!.takeUnretainedValue());
              // compare searched item URL passed in argument with next item URL
              if(nextItemUrl!.takeRetainedValue() == path) {
                  foundLoginItem = nextLoginItem;
              }
          }
      }
  
      return foundLoginItem;
  }
  
  func getLastLoginItemInList() -&gt; LSSharedFileListItem! {
  
      // Copy all login items in the list
      let loginItems : NSArray = LSSharedFileListCopySnapshot(loginItemsList, nil).takeRetainedValue() as NSArray;
      if(loginItems.count &gt; 0) {
          let lastLoginItem = loginItems.lastObject as! LSSharedFileListItem;
  
          print("Last login item is: ", lastLoginItem);
          return lastLoginItem
      }
  
      return kLSSharedFileListItemBeforeFirst.takeRetainedValue();
  }
  
  func isLoginItemInList(_ path : CFURL) -&gt; Bool {
  
      if(getLoginItem(path) != nil) {
          return true;
      }
  
      return false;
  }
  
  static func appPath() -&gt; CFURL {
  
      return NSURL.fileURL(withPath: Bundle.main.bundlePath) as CFURL;
  }
  
   }
  </code></pre></li>
  <li><p>I have used this to turn on/off Login Item by clicking in the checkbox </p>
  
  <pre><code>  let loginItemsList = LoginItemsList();
  
      if( checkboxButton.state == 0) {
          if(!loginItemsList.removeLoginItem(LoginItemsList.appPath())) {
              print("Error while removing Login Item from the list.");
          }
      } else {
          if(!loginItemsList.addLoginItem(LoginItemsList.appPath())) {
              print("Error while adding Login Item to the list.");
          }
      }
  </code></pre></li>
  <li><p>I have run it in Debug mode (Xcode Play button) and try to archive it and export to /Applications folder if it matters, but this approach also doesn't work. </p></li>
  <li><p>Console-printed messaged. Error means that the function Inserting Login Item returns nil.</p></li>
  </ol>
  
  <p><a href="https://i.stack.imgur.com/Y5veG.png" rel="noreferrer"><img src="https://i.stack.imgur.com/Y5veG.png" alt="enter image description here"></a></p>
  
  <p>So after that I even try to implement this (from some stackoverflow example) using Objective-C (as there is many Unmanaged&lt;> in Swift)
  So I added new .m and .h file and Bridging-Header.h and then a code like this: </p>
  
  <pre><code>- (void)enableLoginItemWithURL:(NSURL *)itemURL
  {
      LSSharedFileListRef loginListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
  
      if (loginListRef) {
          // Insert the item at the bottom of Login Items list.
          LSSharedFileListItemRef loginItemRef = LSSharedFileListInsertItemURL(loginListRef,
                                                                               kLSSharedFileListItemLast,
                                                                               NULL,
                                                                               NULL,
                                                                               (__bridge CFURLRef) itemURL,
                                                                               NULL,
                                                                               NULL);
          if (loginItemRef) {
              CFRelease(loginItemRef);
          }
          CFRelease(loginListRef);
      }
  }
  </code></pre>
  
  <p>Simple (just insertion) without any bells and whistles. 
  It also has the same issue that <strong><code>LSSharedFileListInsertItemURL</code></strong> returns nil and Login Item is not added to <strong>System Preferences > Users &amp; Groups > Login Items.</strong> </p>
  
  <p>So any idea why I cannot make this work?</p>
  
  <p><strong>UPDATE 1</strong></p>
  
  <p>I have tried to implement application using first approach (helper Launcher application inside Main application) on another computer iMac (MacOS Sierra and the newest XCode 8.3) and it seems to work there correctly so maybe there is something wrong with my OS or Xcode (provisioning profiles, signing of app or whatever) On MacBook Air where this approach doesn't work I am using OS X El Capitan 10.11.5 and Xcode 8.0. </p>
  
  <p>Watch how it works here: 
  <a href="https://youtu.be/6fnLzkh5Rbs" rel="noreferrer">https://youtu.be/6fnLzkh5Rbs</a>
  and testing 
  <a href="https://www.youtube.com/watch?v=sUE7Estju0U" rel="noreferrer">https://www.youtube.com/watch?v=sUE7Estju0U</a></p>
  
  <p>The second approach doesn't work also on my iMac returning the <strong>nil</strong> while doing <code>LSSharedFileListInsertItemURL</code>. So I don't know why that is happening. </p>
  
  <p>Watch how it works here: 
  <a href="https://youtu.be/S_7ctQLkIuA" rel="noreferrer">https://youtu.be/S_7ctQLkIuA</a></p>
  
  <p><strong>UPDATE 2</strong> </p>
  
  <p>After upgrade to macOS Sierra 10.12.5 from El Capitan 10.11.5 and using Xcode 8.3.2 instead of Xcode 8.0.0 the second approach also happens to work correctly and is adding Login Items to System Preferences > Users &amp; Groups > Login Items 
  <strong>IMPORTANT!</strong> To work this approach with <code>LSSharedFileListInsertItemURL</code> needs to disable <strong>App Sandboxing</strong>! Like in the video below: 
  <a href="https://youtu.be/UvDkby0t_WI" rel="noreferrer">https://youtu.be/UvDkby0t_WI</a></p>