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.
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.
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