How can I remove Dock item in Yosemite?

G’day scripters

I’m using the code below to add a new Dock item, but when coding with Yosemite ASObjC, every new build adds a new item to the Dock.

What I need, (especially for adding future updates), is to remove the old Dock item, before adding a new one.

Any thoughts please?

Regards

Santa


try
				tell application "Dock" to quit
			end try
			set item_path to path to current application as alias
			set item_path to POSIX path of item_path
			do shell script "defaults write com.apple.dock persistent-apps -array-add '<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>" & item_path & "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>'"
			try
				tell application "Dock" to activate
			end try
			

Model: Late 2014 retina i7, Yosemite
AppleScript: 2.4
Browser: Safari 600.2.5
Operating System: Other

I find it much easier to use System Events to manipulate plist. This little script will allow you to inspect the structure of any plist:

set prefsFile to (choose file) as text
tell application "System Events"
	set pfRec to value of property list file prefsFile
end tell

In Dock’s plist, the item which contains an application’s name is addressed like so:

set prefsFile to ((path to preferences from user domain) as text) & "com.apple.dock.plist"
tell application "System Events"
	set dockRec to (value of property list file prefsFile)
	set appRecords to |persistent-apps| of dockRec
	
	repeat with anAppRecord in appRecords
		log |file-label| of |tile-data| of anAppRecord
	end repeat
	
end tell

After you’ve made changes you write the result back out:

tell application "System Events" to set (value of property list file prefsFile) to dockRec -- save the data

It’s likely to be unreliable, whatever you do. Applications write their preferences to a daemon, which in turn updates the property list file in preferences. There’s no guarantee of when that update happens, and in Yosemite, the delay can be significant.

Edited: Actually, I overlooked the fact that you’re using defaults, so you’re probably fine. It’s when you write to the .plist file directly that problems occur.

If you just want to avoid adding duplicates, you could read the existing list and check for your path first.

G’day, and thank you.

Alastor, as I’m using Yosemite, I want to avoid the GUI.

Shane, I can check for and not add new Dock entries, but the old Dock entry still points to a non-existant file, and so shows as a question mark when clicked on, not even asking if I want to resolve it.

With Yosemite, the old item needs to be removed, and replaced with a new item, but how, please?

Anyone?

Regards

Santa

Model: Late 2014 retina i7, Yosemite
AppleScript: 2.4
Browser: Safari 600.2.5
Operating System: Other

You could try using defaults read to get the persistent-apps array, delete the old value from it, and write it back. Or replace the old item, and write it back.

G’day again, and thanks Shane

I got the original shell script from the net, I think from this site, but I haven’t the foggiest on how to write a shell script on removing an item.

I’ve experimented with…


try
	tell application "Dock" to quit
end try
do shell script ("defaults write com.apple.dock persistent-apps -array-delete '<dict><key>tile-data</key><dict><key>file-label</key> <string>" & "Mail Manager" & "</string> /key><integer>0</integer></dict></dict></dict>'")
try
	tell application "Dock" to activate
end try

But don’t know what the heck I’m doing! I’ve realised setting the path to the item is useless, cause I’ve already deleted it, so assume I’ve got to somehow refer to the item by the ‘file-label’, which has the item name. However, I don’t know if the ‘array-delete’ is correct, let alone how to address the ‘file-label’.

i also assume I’ve got to refer to the item by it’s dictionary matching item number, but how?

Can anyone help me out, please?

Regards

Santa

Model: Late 2014 retina i7, Yosemite
AppleScript: 2.4
Browser: Safari 600.2.5
Operating System: Other

Start by going to Terminal and typing man defaults. That tells you how to read and write. You will need to read the existing value into a string, change it how you want, then write it back.

G’day

I’ve tried to understand the Man pages, and spent the last few days on and off experimenting with code, with no success, so I thought I’d try and just delete the existing Dock item and replace it, but also no luck.

I simply have no idea what I’m doing, and searching the web for examples has proved fruitless.

As this is the last cosmetic item ti be resolved in my App, could someone please, please advise me on the correct code, before I lose my blasted mind.

Regards

Santa

My latest try


tell application "Dock" to quit
set prefsFile to (((path to preferences from user domain) as text) & "com.apple.dock.plist") as alias
tell application "System Events"
	prefsFile as text
	set poArray to (value of property list item "persistent-apps" of property list file the result)
	repeat with i from (count items of poArray) to 1 by -1
		set poItem to item i of poArray
		set tileDataRec to |tile-data| of poItem
		set tileName to |file-label| of tileDataRec
		if tileName = "Mail Manager" then
			do shell script "defaults write  com.apple.dock persistent-apps -array-delete   '<domain>   persistent-apps <key>" & ("Item " & (i - 1) as text) & "'"
		end if
 	end repeat
end tell

Model: Late 2014 retina i7, Yosemite
AppleScript: 2.4
Browser: Safari 600.2.5
Operating System: Other

Hi Santa,

AS Shane said, everything is unreliable. A simple way to remove something from Dock is to gui script Dock.

You can use a macro to remove items from Dock with System Events if you want.

gl,
kel

Kel, I want to re-vist this if possible.

I’m finding under El Capitan, when a dock icon is dragged off, it’s still listed in the persistent-icons .plist. I need to, if possible, either remove it from the .plist, and restore it, or, use the GUI to restore, but how.

Any answers please?

Regards

Santa


tell application "Dock"
	activate
	tell application "System Events" to tell process "Dock"
		try
			# click UI element "Microsoft Excel" of list 1
			tell UI element "Mail Manager" of list 1
				set visible to true
			end tell
			-- modify offsets if hot spot is not centered:
		end try
	end tell
	quit
end tell

AND, the script that worked under Yosemite, but locks up under El Capitan, as the dragged off “Mail Manager” .plist item remains behind.


set persistentAppsList to |persistent-apps| of pListItems
set dockAppsList to {}
try
	repeat with thisRecord in persistentAppsList
		set theTileData to |tile-data| of thisRecord as record
		if (|file-label| of theTileData as text) is "Mail Manager" then
			set end of dockAppsList to (|file-label| of theTileData as text)
		end if
	end repeat
end try
try
	tell application "Dock" to quit
end try
if "Mail Manager" is not in dockAppsList then
	do shell script ("sleep 0.2")
	try
		set item_path to ((path to applications folder) & "Mail Manager:Mail Manager.app" as text) as alias 
	end try
	do shell script ("sleep 0.2")
	try
		set item_path to POSIX path of item_path
		do shell script "defaults write com.apple.dock persistent-apps -array-add '<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>" & item_path & "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>'"
	end try
end if

Model: late 2014 i7 iMac
AppleScript: 2.8.1
Browser: Safari 601.4.4
Operating System: Mac OS X (10.10)

Hello,

Perhaps that…

-- Replace app_name by the name of the desired application.

tell application "app_name" to quit
delay 0.5
tell application "System Events"
	tell UI element "app_name" of list 1 of process "Dock"		
		if not (exists) then return		
		perform action "AXShowMenu"
		click menu item "Options" of menu 1
		click menu item "Remove from Dock" of menu 1 of menu item "Options" of menu 1
	end tell
end tell

… but how to hide the menu while the operation?

G’day all,

After several days trying, and with help from members of the Applescript developed list, particularly Shane Stanly, This code, that generates a single icon for an app, in the dock, was achieved.


use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions

# Note my App name is 'Mail Manager.app', and it's located in a 'Mail Manager' folder in Applications.

on installDockIcon()
	-- get the info we need
	set defaultsObject to current application's NSUserDefaults's alloc()'s init()
	set theInfoRecord to (defaultsObject's persistentDomainForName:"com.apple.dock")
	set persistentAppsArray to theInfoRecord's objectForKey:"persistent-apps"
	set tileDataArray to persistentAppsArray's valueForKey:"tile-data"
	set theLabels to tileDataArray's valueForKey:"file-label"
	-- check for match
	set hasMatch to (theLabels's containsObject:"Mail Manager") as boolean
	set existsMMDockIcon to my installDockItemsTest()
	if not hasMatch or not existsMMDockIcon then
		-- make new tile-data record
		set item_path to (path to applications folder as text) & "Mail Manager:Mail Manager.app"
		set item_path to POSIX path of item_path
		set newValue to {|file-data|:{_CFURLString:item_path, _CFURLStringType:0}}
		-- add it to the old tile-data array
		set tileDataArray to tileDataArray's arrayByAddingObject:newValue
		-- add new tile-data array to the persistent-apps array
		set persistentAppsArray to persistentAppsArray's arrayByAddingObject:tileDataArray
		-- update theInfoRecord with the new persistent-apps array
		set theInfoRecordNew to (theInfoRecord's mutableCopy()) -- make mutable copy so you can change it
		theInfoRecordNew's setObject:persistentAppsArray forKey:"persistent-apps"
		-- store the new value
		defaultsObject's setPersistentDomain:theInfoRecordNew forName:"com.apple.dock"
		tell application "Dock" to quit
	end if
end installDockIcon

on installDockItemsTest()
	try
		tell application "System Events"
			tell process "Dock"
				set t to (title of UI elements of list 1)
				if theTest contains {"Mail Manager"} then return true
			end tell
		end tell
	end try
	return false
end installDockItemsTest

Oops, posted the above too soon. Turns out it can trash your Persistent Apps preferences.

This older script, with some of Shanes alterations, works without hassles.

Sorry about that!

Regards

Santa


use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions

my installDockIcon("Mail Manager")

on installDockIcon(theApp)
	try
		set item_path to ((path to applications folder) & "Mail Manager:" & theApp & ".app" as text) # NOTE: my App is  in a folder called 'Mail Manager'
	end try
	try
		
		set theInfo to (current application's NSUserDefaults's alloc()'s init()'s persistentDomainForName:"com.apple.dock") as record
		set theMatches to get theInfo's |persistent-apps|
		set MatchingList to {}
		set x to 0
		repeat with thisRecord in theMatches
			set x to x + 1
			set theTestMatch to get thisRecord's |tile-data|
			set theMMMatch to theTestMatch's |file-label|
			if theMMMatch as text is theApp then
				set end of MatchingList to item x of theMatches
			end if
		end repeat
		set existsMMDockIcon to my installDockItemsTest(theApp)
		if (count of MatchingList) < 1 or not existsMMDockIcon then
			try
				set item_path to POSIX path of item_path
				do shell script "defaults write com.apple.dock persistent-apps -array-add '<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>" & item_path & "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>'"
				tell application "Dock" to quit # Automatically re-starts
			end try
		end if
	end try
end installDockIcon

on installDockItemsTest(theApp)
	try
		tell application "System Events"
			tell process "Dock"
				set t to (title of UI elements of list 1)
				if theTest contains {theApp} then return true
			end tell
		end tell
	end try
	return false
end installDockItemsTest

Is writing plist files with Applescript just broken now?

I’ve got a similar issue. We deploy branches of code for testing, so sometimes a user is moved to a whole different suite of our apps. All our internal references between apps are consistent in-branch, but sometimes users add applications to the dock - indeed, it’s handy for some of these apps to live in the dock. But then the user ends up with their dock copy pointing to the wrong branch… whatever apps are in their dock now don’t change when they change branches. Of course we can tell them to change them, but user behavior is usually the hardest thing to modify.

I’ve been making various attempts to remove the Dock items from the wrong branch automatically and add in the identical ones from the right branch. Adding them is no problem, it’s getting rid of the ones that from the old branch that I can’t figure out.

Any time I attempt to write the com.apple.dock.plist file, it ends up being 0 bytes.

It looks like scripting dockutil https://github.com/kcrawford/dockutil would work, but I’m always loathe to add another dependency that must be installed on all end user systems.

Here’s one of my scripting attempts, based on the work of alastor933 above. It all looks great until the last line doing the write.

set prefsFile to ((path to preferences from user domain) as text) & "com.apple.dock copy.plist"
set removeApps to {"TextEdit"}
tell application "System Events"
	set dockRec to (value of property list file prefsFile)
	set appRecords to |persistent-apps| of dockRec
	set newAppRecord to {}
	repeat with anAppRecord in appRecords
		if |file-label| of |tile-data| of anAppRecord is not in removeApps then copy contents of anAppRecord to end of newAppRecord
	end repeat
	set |persistent-apps| of dockRec to newAppRecord
	set (value of property list file prefsFile) to dockRec
end tell

If I’m reading the example with Shane’s code correctly, the ASObjC still is not writing the preferences file, only reading it so they can parse whether or not the App in question is already in the dock. The write only occurs via “do shell script defaults write…,” which already works fine for me… it’s the removal I can’t figure out.

Ah, I found this very relevant looking thread:

https://macscripter.net/viewtopic.php?id=41893

But I’m having trouble with the ASObjC handlers… I think how ASObjC works has changed since 2013… I can’t get them to work in-script with “Use Framework Foundation,” I get:


And in a library, I get "script [library name] doesn’t understand the “readPlistAt” message.

I don’t think there still is an “click the Bundle Contents button and check the AppleScript/Objective0bjC Library checkbox”

Are you putting the use framework statements in your script libraries that use ASObjC?

Yes. I just use the “Open this Scriplet in your Editor” button on MacScripter to open your script, which does have “Use Framework Foundation” at the top. I saved it as a new library, just called “test” for now.

Then I’m just calling it like this:

set prefsFile to (POSIX path of (path to preferences from user domain)) & "com.apple.dock copy.plist"
tell script "test" to set dockRecC to readPlistAt(prefsFile)

And I get:

The handler in Shane’s original script takes an “interleaved” parameter, not a “positional” one. So:

set prefsFile to (POSIX path of (path to preferences from user domain)) & "com.apple.dock copy.plist"
tell script "test" to set dockRecC to readPlistAt:prefsFile

What Nigel said, but it won’t compile like that – interleaved parameters must follow some kind of possessive. For example:

set dockRecC to script "test"'s readPlistAt:prefsFile

Or use underscores:

tell script "test" to set dockRecC to readPlistAt_(prefsFile)

Thanks!

Obviously, I still haven’t found time to go through Everyday Applescript Objective C.

I had guessed Nigel’s suggestion to try, but hit the error you mentioned.

I also found I can just put the handlers in the script instead of a library and use

set dockRecC to my readPlistAt:prefsPOSIX
  • Tom.