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.
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
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)
-- 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
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.
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”
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)
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
For reference, if anybody else needs to remove items from their Dock, this seems to work:
use framework "Foundation"
use scripting additions
set prefsFile to (POSIX path of (path to preferences from user domain)) & "com.apple.dock.plist"
set removeApps to {"TextEdit"}
set dockRec to my readPlistAt: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
my saveRecord:dockRec toPlistAt:prefsFile
repeat 2 times
do shell script "killall Dock"
delay 3
end repeat
on saveRecord:theRecord toPlistAt:posixPath
set theDict to current application's NSDictionary's dictionaryWithDictionary:theRecord
set thePath to current application's NSString's stringWithString:posixPath
set thePath to thePath's stringByExpandingTildeInPath()
theDict's writeToFile:thePath atomically:true
end saveRecord:toPlistAt:
on readPlistAt:posixPath
set thePath to current application's NSString's stringWithString:posixPath
set thePath to thePath's stringByExpandingTildeInPath()
set theDict to current application's NSDictionary's dictionaryWithContentsOfFile:thePath
return theDict as record
end readPlistAt:
Note, it’s unclear to me why I need to restart the Dock twice with a delay in between, but so far it’s working reliably on several tests with this. I tried different numbers of repeats and different delays… I wasn’t exhaustive about it, but the 2 repeats with a 3 second delay in between seems to be reliable on my machine. Your mileage may vary.
I don’t remember exactly from whom I got the following script. It is designed to remove the application icon in the Dock and is more compact.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
removeDockItem("VLC")
on removeDockItem(dockItemName)
set dockDefaults to current application's class "NSUserDefaults"'s alloc()'s initWithSuiteName:"com.apple.dock"
set apps to dockDefaults's objectForKey:"persistent-apps"
if (apps's |count|() > 0) then
set filter to current application's class "NSPredicate"'s predicateWithFormat:("%K != %@") argumentArray:({"tile-data.file-label", dockItemName})
set appsCopy to apps's filteredArrayUsingPredicate:(filter)
if (appsCopy's |count|() < apps's |count|()) then
tell dockDefaults to setObject:(appsCopy) forKey:("persistent-apps")
tell application "Dock" to quit -- Possibly a bit extreme.
end if
end if
end removeDockItem
Interestingly, the “tell dockDefaults to setObject:(appsCopy) forKey:(“persistent-apps”)” seems to make it so that a single “tell application “Dock” to quit” is sufficient.
I didn’t experiment with this much; I did use the “tell…quit” statement in your script, and maybe the difference was that vs. the “shell…killall” statement. Not important for me to get to the bottom of, this is working better.
I kept the less succinct Applescript Repeat Loop just because I’m familiar with it and can edit it as needed… I’m thinking I might want to use this script to re-arrange the dock to. The idea here is that when a user changes an entire branch of our code to test, the internal references in all our apps are consistent within whatever branch they move to, but if they added an application to the dock, then it’s got a hard-coded path pointing to something on the wrong branch… which can cause trouble.
So I want the script that switches their branch to also find and remove any apps pointing to apps on the old branch, and then add the equivalent script from the new branch.
But if I just run the handler as I have it, and then run a standard shell line to add the correct copy using (do shell script "defaults write …) then it’s going to re-arrange their dock.
So maybe I’ll also have this re-arrange it back for them. I’ll probably wait and see if there are any complaints.
Thanks again, Shane and KniazidisR! Great stuff, super helpful.
In case it’s useful to someone, here’s more complete code that keeps track of which apps were in the dock, and will only add back ones it actually removed.
use framework "Foundation"
use scripting additions
set checkApps to {"TextEdit","Mail","Whatever App Name," "Etc."}
set removedApps to remove_apps_from_dock(checkApps)
tell script "My Additions" to set scriptsFolder to POSIX path of get_scripts_folder()
set missingApps to {}
set appPaths to {}
repeat with anApp in removedApps
try
set thisAppPath to scriptsFolder & anApp & ".app"
set thisAlias to (thisAppPath as POSIX file) as alias
set appPaths to appPaths & thisAppPath
on error
set missingApps to missingApps & anApp
end try
end repeat
if (count of missingApps) > 0 then
set missinglist to ""
repeat with anApp in missingApps
set missinglist to return & anApp & missinglist
end repeat
display dialog "The following applications were in your dock, but don't exist on the new branch. The old copies will be removed from your dock and not replaced." & missinglist
end if
if (count of removedApps) > 0 then
repeat with anAppPath in appPaths
add_app_to_dock(anAppPath)
end repeat
tell application "Dock" to quit
end if
on remove_apps_from_dock(removeApps)
set dockDefaults to current application's class "NSUserDefaults"'s alloc()'s initWithSuiteName:"com.apple.dock"
set appRecord to dockDefaults's objectForKey:"persistent-apps"
set newAppRecord to {}
set removedApps to {}
repeat with anAppRecord in appRecord
set appName to (|file-label| of |tile-data| of anAppRecord) as text
if appName is in removeApps then
set removedApps to removedApps & appName
else
copy contents of anAppRecord to end of newAppRecord
end if
end repeat
tell dockDefaults to setObject:(newAppRecord) forKey:("persistent-apps")
return removedApps
end remove_apps_from_dock
on add_app_to_dock(appPath)
if class of appPath is alias then set appPath to POSIX path of appPath
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>" & appPath & "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>'"
end add_app_to_dock
It calls a “get_scripts_folder()” handler from a library that I’m not including, because it would be localized to an individual’s setup. Replace as appropriate for wherever your apps are.