Best method for auto launch of app after login

What is the best method to launch automatically an app after login of users. From macOS 11 and with API 2 recent version of Xojo.

If you open System Preferences and open the “Users and Groups” item. Click on your account and then the Login Items tab. You can add applications to that list and they will run when you login.

Right click on the dock icon,
Select Options/Open at login

I’m looking to do it programmatically.

You would have to create a launchDemon plist file. Permission write it outside of an installer would be tricky.

1 Like

If you use MBS Xojo Plugins, check the LSSharedFileListMBS class.
I expect it to still work for login items.

The easiest way to create a LaunchDemon is with the app Lingon (Lingon - Peter Borg Apps). It costs 10 $. Afterwards you can create the file in code.

I think that starting with Ventura LaunchDemons can come within the app. LaunchDemons are loaded and unloaded with the Terminal command launchctl. The command changed ages ago but most websites still describe the old version and not the new one.

It’s also trivial to make a Xojo app to handle all this (additionally showing the authentication dialog with the MBS plugin).

I made long time ago with the help of guys on a French forum an AppleScript which I call from Xojo with this code:

If CheckBoxLogItems.Value Then ' I put my App in the Logins items
  PosLogItemSt = AjEffLoginItems(CetAppShPath, "Le_Creer") ' Il n'existait pas, on le crée
Else ' I remove my App from the Logins items
  PosLogItemSt = AjEffLoginItems(CetAppShPath, "L_Effacer") ' Il existait, on l'efface
End If

.
With
CetAppShPath = App.ExecutableFile.Parent.Parent.Parent.NativePath
.
.
The AppleScript:

-- Même procédures dans MemoDate et LaunchOnceAday pour  AjEffLoginItems  et  LogItExists

on run {item1T, QueFaireT} -- C'est du texte, "Le_Creer" créera le Login Item si n'existe pas et "L_Effacer" l'effacera s'il existe, si vide ne fera que renvoyer si existe ou non par sa position (-1 n'existe pas) , "Test" le créera si n'existe pas et l'effacera sinon
	(* Pour Test
	tell application "Finder"
		set item1 to choose file of type "APPL" with prompt "Choose the Application :" default location (path to applications folder)
		if item1 exists then
			-- set item1Nom to displayed name of item1 as text
			set item1T to (POSIX path of item1) as text -- Note : item1 est un alias   quoted form of
			if ((characters -1 thru -1 of item1T) as text) is "/" then set item1T to (characters 1 thru -2 of item1T) as text
		end if
		set QueFaireT to "Test"
	end tell -- Finder
	activate -- Editeur AppleScript
	*)
	
	set CeLogItNoT to (AjEffLoginItems(item1T, QueFaireT) of me) as text
	
	CeLogItNoT -- A pour effet de renvoyer cette valeur à RealBasic  On est obligé de retourner du texte
end run

on AjEffLoginItems(CeShPath, QueFaire) -- ATTENTION : parfois (pour les applications notemment), il y a un '/' à la fin du path et parfois non. Ca pose problème car pas identique
	tell application "System Events"
		try
			set NbLoginItems to count of login items
		on error
			set NbLoginItems to -1
		end try
	end tell
	set CeLogItNo to LogItExists(CeShPath, NbLoginItems) of me -- , item1Nom
	-- display dialog "Ce Login Item existe au n° : " & CeLogItNo & " (-1 si n'existe pas)." & return & "Path : '" & CeShPath & "' , class : " & (class of CeShPath) & return & "QueFaire : " & QueFaire -- & return & "Nom : '" & item1Nom & "'"
	if CeLogItNo is -1 then -- Le Login Item n'existe pas
		if (QueFaire is "Le_Creer") or (QueFaire is "Test") then
			tell application "System Events"
				try
					make new login item at end of login items with properties {path:CeShPath, hidden:false} -- , kind:volume}
					set CeLogItNo to NbLoginItems + 1 -- Puisque créé en dernière position
				on error
					-- CeLogItNoreste à -1
				end try
			end tell
			-- else  alors c'est "L_Effacer" et on pourrait faire une alerte pour dire que pas logique puisque n'existait pas
		end if
	else -- Le Login Item existe
		if (QueFaire is "L_Effacer") or (QueFaire is "Test") then
			tell application "System Events"
				try
					delete login item CeLogItNo
				on error
					-- CeLogItNoreste est quand même mis à -1
				end try
				set CeLogItNo to -1
			end tell
			-- else  alors c'est "Le_Creer" et on pourrait faire une alerte pour dire que pas logique puisque existait déjà
		end if
	end if
	if QueFaire is "Test" then
		tell application "System Preferences" to activate
		delay 1 -- secondes
		activate -- Finder
	end if
	return CeLogItNo
end AjEffLoginItems

on LogItExists(CeShPath, NbLogIt) -- , CeNom
	set NoLogIt to -1 -- N'existe pas
	tell application "System Events"
		-- set TsNomLogin to the name of every login item
		-- TsNomLogin
		-- set TsPathLogin to the path of every login item
		-- TsPathLogin
		-- tell application "Finder" to display dialog "Nb Log Items = " & NbLogIt as text
		repeat with iNbre from 1 to NbLogIt
			-- display dialog ("'" & (name of login item iNbre) as text) & "'" & return & "'" & ((path of login item iNbre) as text) & "'"
			if (((path of login item iNbre) as text) is CeShPath) then
				-- display dialog ("'" & (name of login item iNbre) as text) & "' existe." & return & "iNbre = " & (iNbre as text)
				set NoLogIt to iNbre
				exit repeat
			end if
		end repeat
		(* Je ne fais pas ça car si jamais il y a 2 éléments avec le même nom mais un path différent
		if login item CeNom exists then
			-- display dialog ((path of login item CeNom) as text) & return & CeShPath
			set DrapLogItExists to (((path of login item CeNom) as text) is CeShPath) as boolean
		else
			set DrapLogItExists to false
		end if *)
	end tell
	return NoLogIt
end LogItExists

(*
tell application "System Events"
	-- LOGIN ITEMS
	get the properties of every login item
	-- {{class:login item, path:"/Users/sal/Desktop/View Remote Screen.app", hidden:false, kind:"Application", name:"View Remote Screen"}}
	-- Adding a login item for the current user
	make new login item at end of login items with properties {path:"/Applications/Dictionary.app", hidden:false}
end tell
*)

Yep, this still works. I use it in a couple of applications to make launch on login available as a preference within the app itself.

I’ve been looking into the LSSharedFileListMBS class - but how do I tell where the app is, if it’s not in the Applications folder? If it’s launched from the Downloads or Desktop folder, or the user moves it, the first line of code below won’t work.

Is there a way for the app to find out where it resides?

Dim app As FolderItem = SpecialFolder.Applications.Child("MyApplication.app")

// get list object
Dim l As New LSSharedFileListMBS(LSSharedFileListMBS.kSessionLoginItems)

// insert file
Dim item As LSSharedFileListItemMBS = l.InsertFile(l.kLSSharedFileListItemBeforeFirst, "MyApplication", Nil, app)

// check error
If l.Lasterror = 0 Then
  MsgBox "OK"
Else
  MsgBox "Failed: "+Str(l.Lasterror)
End If

See app.executableFile, where you may need to use .parent on to find the app bundle.

1 Like

I missed the “.parent” part until now. In my case, it was “app.executableFile.parent.parent.parent” to get to the main app.
Terminal is no longer launching on startup, and the app is.