Help: Using AppleScript to remove dock icon

I’m currently stuck, to remove a Dock icon, I know I need to edit the dock.plist. Problem is, I dont know how.
What is the easiest way to remove the dock icon with applescript? I cannot find any sources on the internet.

I used this code to add a folder to the dock


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>/Applications/myApp.app/</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>'"

I wonder if I can just alter this code to remove it? is so, how?

I’ve been stuck with this problem for almost a week. any help will be very much appreciated.
Thanks

Here is the AppleScriptObjC code, but it has 1 error and don’t work yet:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property NSString : a reference to NSString of current application
property NSUserDefaults : a reference to NSUserDefaults of current application
property NSDictionary : a reference to NSDictionary of current application

set DockIconLabel to "VLC"
set DockIconLabel to NSString's stringWithString:DockIconLabel

set userDefaults to NSUserDefaults's standardUserDefaults()
set dockDict to userDefaults's persistentDomainForName:"com.apple.dock"
set dockApps to dockDict's valueForKey:"persistent-apps"

repeat with anApp in dockApps
	set fileDict to (anApp's valueForKey:"tile-data")
	set iconLabel to (fileDict's valueForKey:"file-label")
	if DockIconLabel = iconLabel then
		(dockApps's removeObject:anApp)
		NSUserDefaults's resetStandardUserDefaults
		exit repeat
	end if
end repeat

This code is my first attempt to convert Objective C code to AppleScriptObjC. The Objective C source is HERE. The error is related to this code line (something with mutable array):

(dockApps's removeObject:anApp)

I don’t know how to fix that. Need help.
Other Objective-C example is without repeat loop, but I don’t know preidcate’s syntax as well:

(BOOL)removeApplicationFromDock:(NSString *)name {
    NSDictionary *domain = [self persistentDomainForName:@"com.apple.dock"];
    NSArray *apps = [domain objectForKey:@"persistent-apps"];
    NSArray *newApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"not %K CONTAINS %@", @"tile-data.file-data._CFURLString", name]];
    if (![apps isEqualToArray:newApps]) {
        NSMutableDictionary *newDomain = [domain mutableCopy];
        [newDomain setObject:newApps forKey:@"persistent-apps"];
        [self setPersistentDomain:newDomain forName:@"com.apple.dock"];
        return [self synchronize];
    }

@KniazidisR

Not sure but maybe you would have to trigger
userDefaults’s registerDefaults

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 9 aout 2019 16:36:05

I fixed the script but the icon remains in the Dock:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property NSString : a reference to NSString of current application
property NSUserDefaults : a reference to NSUserDefaults of current application
property NSDictionary : a reference to NSDictionary of current application

set DockIconLabel to "VLC"
set DockIconLabel to NSString's stringWithString:DockIconLabel

set userDefaults to NSUserDefaults's standardUserDefaults()
set dockDict to userDefaults's persistentDomainForName:"com.apple.dock"
set dockApps to dockDict's valueForKey:"persistent-apps"
set newDockApps to dockApps's mutableCopy()

repeat with anApp in dockApps
	set fileDict to (anApp's valueForKey:"tile-data")
	set theLabel to (fileDict's valueForKey:"file-label")
	if (theLabel's isEqualTo:DockIconLabel) then
		(newDockApps's removeObject:anApp)
		exit repeat
	end if
end repeat

NSUserDefaults's resetStandardUserDefaults()

What else am I missing? Please, help.

Thanks for reply, but what should be syntax in my script? This throws error:

userDefaults's registerDefaults

Look at message #12 in:
https://macscripter.net/viewtopic.php?id=30476

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 9 aout 2019 19:24:08

I registered dictionary changes - I replaced the last code line with the following, but the icon remains on the Dock.


dockDict's setObject:newDockApps forKey:"persistent-apps"
userDefaults's registerDefaults:dockDict

So. There is only one way to solve the problem - the GUI sctipting … I will provide a solution soon, although this is not what I was hoping for …

GUI scripting variant (work fine):


set theAppToRemoveName to "VLC" -- for testing purposes

tell application "System Events"
	
	if (application process theAppToRemoveName exists) then
		set theMenuItemName to "Keep in Dock"
	else
		set theMenuItemName to "Remove from Dock"
	end if
	
	tell application process "Dock"
		try
			tell list 1 to tell UI element theAppToRemoveName
				perform action "AXShowMenu"
				click menu item "Options" of menu 1
				click menu item theMenuItemName of menu 1 of menu item "Options" of menu 1
			end tell
		on error
			display dialog "The Script should be terminated.
		 The App with specified name doesn't exist on the Dock." buttons "OK"
			return
		end try
	end tell
	
end tell

NOTE: if the app is running, then its icon will be removed when you quit this app.

You’re missing the way defaults work. The defaults system is like a database. You can make changes, but you have no control over when apps read it. Some values they might read repeatedly, and others they just read the first time they need them.

Hi KniazidisR .

The current Xcode documentation says that resetStandardUserDefaults() has no effect and shouldn’t be used.

Here’s my own literal translation and correction of the Objective C source to which you linked. It seems to work:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

on removeDockItem(dockItemName)
	set dockItemName to current application's class "NSString"'s stringWithString:(dockItemName)
	
	set userDefaults to current application's class "NSUserDefaults"'s standardUserDefaults()
	set dockDict to (userDefaults's persistentDomainForName:("com.apple.dock"))'s mutableCopy() -- Seems to be mutable anyway.
	set apps to (dockDict's valueForKey:("persistent-apps"))
	if (apps's |count|() > 0) then -- More likely than 'if (apps is not missing value) …' ?
		set appsCopy to apps's mutableCopy()
		set |modified| to false
		repeat with anApp in appsCopy
			set fileDict to (anApp's valueForKey:("tile-data"))
			if (fileDict is not missing value) then
				set appName to (fileDict's valueForKey:("file-label"))
				if (appName's isEqualToString:(dockItemName)) then
					tell appsCopy to removeObject:(anApp)

					set |modified| to true
					exit repeat
				end if
			end if
		end repeat
		
		if (|modified|) then
			tell dockDict to setValue:(appsCopy) forKey:("persistent-apps") -- Added.
			tell userDefaults to setPersistentDomain:(dockDict) forName:("com.apple.dock")
			-- current application's class "NSUserDefaults"'s resetStandardUserDefaults() -- Legacy. Shouldn't be used.
			tell application "Dock" to quit -- Possibly a bit extreme.
		end if
	end if	
end removeDockItem

removeDockItem("Launchpad")

However, less literally, it’s possible to use a predicate instead of a repeat:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

on removeDockItem(dockItemName)
	set dockItemName to current application's class "NSString"'s stringWithString:(dockItemName)
	
	set userDefaults to current application's class "NSUserDefaults"'s standardUserDefaults()
	set dockDict to (userDefaults's persistentDomainForName:("com.apple.dock"))'s mutableCopy() -- Seems to be mutable anyway.
	set apps to (dockDict's valueForKey:("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 dockDict to setValue:(appsCopy) forKey:("persistent-apps")
			tell userDefaults to setPersistentDomain:(dockDict) forName:("com.apple.dock")
			tell application "Dock" to quit -- Possibly a bit extreme.
		end if
	end if
end removeDockItem

removeDockItem("Launchpad")

Nigel,

The predicate in your last script is problematic – it’s a sort of double negative. Try removing the first !. As it stands, it’s a bit on the destructive side.

(Off to rebuild my Dock…)

Here’s Nigel’s last script, reworked to use initWithSuiteName: rather than persistentDomainForName:. The advantage is that you don’t have to read/write a dictionary for the whole domain, but rather that of a single key. There’s probably no discernible difference in this case, but in other cases it could be significant. (I’ve also changed the predicate as above.)

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

Ouch! Thanks Shane. Sorry about your Dock. :frowning: It’s a long story involving trying several different things to get the predicate to work, with the eventual solution being to have the key path as an external argument and to leave off “self.”. While I was posting the script, I noticed I still had the predicate string as “!(%K == %@)”, which I’d changed from “%K != %@” during my experiments. I decided to change it back, but by then it was 03:11 in the morning ….

Now corrected.

Your version of the handler simplifies things a bit! :slight_smile:

Oh, I’m just glad of the confirmation that these things don’t happen just to me :slight_smile:

By the way you can use the old underscore syntax to simulate the ObjC form:

set filter to current application's class "NSPredicate"'s predicateWithFormat_("%K != %@", "tile-data.file-label", dockItemName)

Thanks, Stefan. That looks as if it could be slightly more efficient than the form with argumentArray:. It would certainly look better when there’s only one argument! :slight_smile: