Skip to content

The Eclectic Light Company

Macs, painting, and more
Main navigation
  • Downloads
  • M1 & M2 Macs
  • Mac Problems
  • Mac articles
  • Art
  • Macs
  • Painting
hoakley May 23, 2018 Macs, Technology

A simpler way to control an app’s Login Item

If you write a menubar or ‘Task Bar’ app, it’s an App Store requirement that it doesn’t automatically install itself as a LaunchAgent or in Login Items. Users should be given the choice as to whether they want your tool to run each time they start their Mac up.

Although adding an app to Login Items is not a difficult task for the user – and for apps in the Dock this is even easier – it is kinder to provide this control as a feature. It is also one of the most frustratingly complex parts of writing such small tools, as the best solutions appear reliant on a helper app.

Daunted by what I read in this item on Stack Overflow, this tutorial, and another tutorial, I wanted something less fiddly which would work for a menubar app which is not sandboxed. This is what I came up with.

First, we need an outlet for the menu command, so that we can add or remove a status tick √ by it:
@IBOutlet weak var runonStartupCommand: NSMenuItem!

My original plan was to call three simple lines of AppleScript to get a list of what is installed in Login Items, to add the app to that list, and to remove it from the list. The first of these is the most important: if you can’t find out whether your app is already a Login Item, then you can’t use this method.

Unfortunately, support for running AppleScript from Swift 4.0 has several problems. It leaks memory, I gather, although as users are hardly likely to toggle this control very often, that shouldn’t be a worry here. The wall that I hit here was getting the list of Login Items back from running the script – all attempts to do so either caused a crash, or returned a blank. So the code to run the two other AppleScript lines doesn’t even bother looking for a result:
func doScriptScript(source: String) {
if let appleScript = NSAppleScript(source: source) {
var errorDict: NSDictionary? = nil
var _ = appleScript.executeAndReturnError(&errorDict)
} }

The only reliable and robust way to get the list of Login Items was to call a shell script to run the AppleScript using osascript:
func doShellScript() -> String {
let theLP = "/usr/bin/osascript"
let theParms = ["-e", "tell application \"System Events\" to get the name of every login item"]
let task = Process()
task.launchPath = theLP
task.arguments = theParms
let outPipe = Pipe()
task.standardOutput = outPipe
task.launch()
let fileHandle = outPipe.fileHandleForReading
let data = fileHandle.readDataToEndOfFile()
task.waitUntilExit()
let status = task.terminationStatus
if (status != 0) {
return "Failed, error = " + String(status)
}
else {
return (NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String)
} }

The Action to run when the user selects the menu command is then:
@IBAction func runonStartupClicked(_ sender: NSMenuItem) {

First check whether the app is already in Login Items:
let theStr = self.doShellScript()
if theStr.contains("Bailiff") {

If it is, remove it from there
let theCmd4 = "tell application \"System Events\" to delete login item \"Bailiff\""
self.doScriptScript(source: theCmd4)
} else {

If it isn’t, add it. To do this we need to know the app’s bundle path, and embed that in the script:
let theCmd1 = "tell application \"System Events\" to make login item at end with properties {path:\""
let theCmd2 = "\", hidden:false}"
let thePath = Bundle.main.bundlePath
self.doScriptScript(source: (theCmd1 + thePath + theCmd2))
}

Now check again whether the app is in Login Items. If it is, put a √ by the menu command; if not, don’t.
let theStr2 = self.doShellScript()
if theStr2.contains("Bailiff") {
runonStartupCommand.state = NSControl.StateValue.on
} else {
runonStartupCommand.state = NSControl.StateValue.off
} }

bailiff10

Finally, we need to add the same check when the app starts up, to know whether to put a √ by that command:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// other stuff first, then
let theStr2 = self.doShellScript()
if theStr2.contains("Bailiff") {
runonStartupCommand.state = NSControl.StateValue.on
} else {
runonStartupCommand.state = NSControl.StateValue.off
} }

The only potentially puzzling behaviour which this might cause is if a user is already running Bailiff, and then changes its status in the Login Items directly in the Users & Groups pane. Coping with that more gracefully would require notification from that pane of the change in status, so that the menu item could be refreshed. For now, I’ll assume that the chances of a user doing that are low, and in any case the checks in the Action function should resync this when the user tries the command next.

Share this:

  • Twitter
  • Facebook
  • Reddit
  • Pinterest
  • Email
  • Print

Like this:

Like Loading...

Related

Posted in Macs, Technology and tagged AppleScript, LaunchServices, Login Item, scripting, Swift, Swift 4.0. Bookmark the permalink.

2Comments

Add yours
  1. 1
    rogparish on May 23, 2018 at 12:10 pm

    OMG, what a horrible language! And people used to complain that COBOL was verbose! This crap wouldn’t be any worse if it was written in Cyrillic!

    LikeLike

    • 2
      hoakley on May 23, 2018 at 12:14 pm

      It may just be my poor style, of course.
      I actually rather like Swift for coding. It’s a bit like macOS, though – for each task, there are multiple ways of accomplishing it. Some are pedestrian, as I tend to use, some very succinct and elegant, but opaque until you know the language well.
      It is at least far better than Objective-C and C++.
      Howard.

      LikeLike

·Comments are closed.

Quick Links

  • Downloads
  • Mac Troubleshooting Summary
  • M1 & M2 Macs
  • Mac problem-solving
  • Painting topics
  • Painting
  • Long Reads

Search

Monthly archives

  • January 2023 (74)
  • December 2022 (74)
  • November 2022 (72)
  • October 2022 (76)
  • September 2022 (72)
  • August 2022 (75)
  • July 2022 (76)
  • June 2022 (73)
  • May 2022 (76)
  • April 2022 (71)
  • March 2022 (77)
  • February 2022 (68)
  • January 2022 (77)
  • December 2021 (75)
  • November 2021 (72)
  • October 2021 (75)
  • September 2021 (76)
  • August 2021 (75)
  • July 2021 (75)
  • June 2021 (71)
  • May 2021 (80)
  • April 2021 (79)
  • March 2021 (77)
  • February 2021 (75)
  • January 2021 (75)
  • December 2020 (77)
  • November 2020 (84)
  • October 2020 (81)
  • September 2020 (79)
  • August 2020 (103)
  • July 2020 (81)
  • June 2020 (78)
  • May 2020 (78)
  • April 2020 (81)
  • March 2020 (86)
  • February 2020 (77)
  • January 2020 (86)
  • December 2019 (82)
  • November 2019 (74)
  • October 2019 (89)
  • September 2019 (80)
  • August 2019 (91)
  • July 2019 (95)
  • June 2019 (88)
  • May 2019 (91)
  • April 2019 (79)
  • March 2019 (78)
  • February 2019 (71)
  • January 2019 (69)
  • December 2018 (79)
  • November 2018 (71)
  • October 2018 (78)
  • September 2018 (76)
  • August 2018 (78)
  • July 2018 (76)
  • June 2018 (77)
  • May 2018 (71)
  • April 2018 (67)
  • March 2018 (73)
  • February 2018 (67)
  • January 2018 (83)
  • December 2017 (94)
  • November 2017 (73)
  • October 2017 (86)
  • September 2017 (92)
  • August 2017 (69)
  • July 2017 (81)
  • June 2017 (76)
  • May 2017 (90)
  • April 2017 (76)
  • March 2017 (79)
  • February 2017 (65)
  • January 2017 (76)
  • December 2016 (75)
  • November 2016 (68)
  • October 2016 (76)
  • September 2016 (78)
  • August 2016 (70)
  • July 2016 (74)
  • June 2016 (66)
  • May 2016 (71)
  • April 2016 (67)
  • March 2016 (71)
  • February 2016 (68)
  • January 2016 (90)
  • December 2015 (96)
  • November 2015 (103)
  • October 2015 (119)
  • September 2015 (115)
  • August 2015 (117)
  • July 2015 (117)
  • June 2015 (105)
  • May 2015 (111)
  • April 2015 (119)
  • March 2015 (69)
  • February 2015 (54)
  • January 2015 (39)

Tags

APFS Apple AppleScript Apple silicon backup Big Sur Blake bug Catalina Consolation Console diagnosis Disk Utility Doré El Capitan extended attributes Finder firmware Gatekeeper Gérôme HFS+ High Sierra history of painting iCloud Impressionism iOS landscape LockRattler log logs M1 Mac Mac history macOS macOS 10.12 macOS 10.13 macOS 10.14 macOS 10.15 macOS 11 macOS 12 macOS 13 malware Mojave Monet Monterey Moreau MRT myth narrative OS X Ovid painting Pissarro Poussin privacy realism Renoir riddle Rubens Sargent scripting security Sierra SilentKnight SSD Swift symbolism Time Machine Turner update upgrade Ventura xattr Xcode XProtect

Statistics

  • 13,763,264 hits
Blog at WordPress.com.
Footer navigation
  • About & Contact
  • Macs
  • Painting
  • Language
  • Tech
  • Life
  • General
  • Downloads
  • Mac problem-solving
  • Extended attributes (xattrs)
  • Painting topics
  • Hieronymus Bosch
  • English language
  • LockRattler: 10.12 Sierra
  • LockRattler: 10.13 High Sierra
  • LockRattler: 10.11 El Capitan
  • Updates: El Capitan
  • Updates: Sierra, High Sierra, Mojave, Catalina, Big Sur
  • LockRattler: 10.14 Mojave
  • SilentKnight, silnite, LockRattler, SystHist & Scrub
  • DelightEd & Podofyllin
  • xattred, Metamer, Sandstrip & xattr tools
  • 32-bitCheck & ArchiChect
  • T2M2, Ulbow, Consolation and log utilities
  • Cirrus & Bailiff
  • Taccy, Signet, Precize, Alifix, UTIutility, Sparsity, alisma
  • Revisionist & DeepTools
  • Text Utilities: Nalaprop, Dystextia and others
  • PDF
  • Keychains & Permissions
  • LockRattler: 10.15 Catalina
  • Updates
  • Spundle, Cormorant, Stibium, Dintch, Fintch and cintch
  • Long Reads
  • Mac Troubleshooting Summary
  • LockRattler: 11.0 Big Sur
  • M1 & M2 Macs
  • Mints: a multifunction utility
  • LockRattler: 12.x Monterey
  • VisualLookUpTest
  • Virtualisation on Apple silicon
  • LockRattler: 13.x Ventura
Secondary navigation
  • Search

Post navigation

Bailiff can now install and uninstall itself as a Login Item
Pierre Bonnard: bustling Paris and the bedroom, 1896-1899

Begin typing your search above and press return to search. Press Esc to cancel.

  • Follow Following
    • The Eclectic Light Company
    • Join 3,130 other followers
    • Already have a WordPress.com account? Log in now.
    • The Eclectic Light Company
    • Customize
    • Follow Following
    • Sign up
    • Log in
    • Copy shortlink
    • Report this content
    • View post in Reader
    • Manage subscriptions
    • Collapse this bar
 

Loading Comments...
 

    %d bloggers like this: