How to add a custom badge to an app icon in the dock

Yes, but we will not edit the original icon itself. We will edit 1 dictionary key value of Info.plist. That is, we change only the link to the icon that we need. And that is temporary. Is it really that dangerous? After all, each application is an object with settings, and not some untouchable stone from Mecca

I’m not 100% sure, but logically, we could add another icon to the existing ones in the Resources folder and refer to it from the Info.plist if necessary.

One of the uses an app’s Info.plist file these days is to declare up-front several privacy related values. Allowing the file to be modified undermines that.

I know that there are applications that change the icons of files and folders to more beautiful ones. I do not know if there are any applications that change application icons … If they exist, how do they do it?

HERE is example of doing that

That is not actually changing the app’s icon – it’s just changing how it appears on your Mac. You can do it AppleScript:

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set picFile to choose file of type {"public.image"} with prompt "Choose an image file"
set theApp to choose file of type {"app"} with prompt "Choose the app to apply it to"

set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:picFile
set ws to current application's NSWorkspace's sharedWorkspace()
ws's setIcon:theImage forFile:(POSIX path of theApp) options:0

The app itself will be completely unchanged.

I think this is what is needed in this case.

Will this update the appearance of the app in the dock (whether it’s currently running or not)? My experience with changing icons using the get info window is that you have to remove the app from the dock and re-add it.

OK, I just tried this out, but without the prompts (which defeat my purpose).

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set picFile to alias "Macintosh HD:Users:<my_username>:.slack_icons:slack.icns"
set theApp to alias "Macintosh HD:Applications:Slack.app"

set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:picFile
set ws to current application's NSWorkspace's sharedWorkspace()
ws's setIcon:theImage forFile:(POSIX path of theApp) options:0

I encountered 2 problems.

  1. As I suspected, the app’s icon update does not get reflected in the dock (until I do something like “right-click-Options…->Show in Finder”).
  2. Script Editor won’t let me save the script with the error “The Document “Untitled.scpt” could not be saved as “clearSlackStatus.scpt”. C and Objective-C pointers cannot be saved in scripts. Compiling the script will reset property values and may resolve this issue.”

I’m sure you would have a ready solution to item 2, b ut I’m not familiar enough with AppleScript to know how to do that.

Regarding item 1, I still have to perform:

[format]rm -f /var/folders///*/com.apple.dock.iconcache
killall Dock[/format]

To make the change effective.

What a ridiculous work-around. I was able to save the script by creating a new empty script, saving it, pasting the code over from the script I couldn’t save, and then save it again. WTH?

You can save script with pointers using Script Debugger instead of Script Editor

I’ve noted that:

[format]rm -f /var/folders///*/com.apple.dock.iconcache[/format]

Removes the unread mail badge… so I’d still like to know how this update can be reflected in the icon in the dock…

That’s correct.

You’ve just pasted the solution: “Compiling the script will reset property values”.

I doubt that it can. The dock icon belongs to the app, not you.

No you can’t – scripts can’t be saved when top-level variables contain pointers, regardless of the editor. The solution is to re-compile, so the saved variables have no value.

But you can avoid the problem using Script Debugger, by turning off Persistent Properties.

If so, can I assign the missing value to the properties at the end of the script and achieve the same effect as recompilation?

Yes. But it’s not just properties – it’s all top-level variables that contain pointers.

Another solution, unless properties and/or globals are absolutely necessary in a running script (which I find they rarely are), is to make all the variables local, and therefore non-persistent, by putting the top-level run code into an ordinary handler:

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

main() -- The sole instruction in the implicit run handler.

on main()
	set picFile to alias "Macintosh HD:Users:<my_username>:.slack_icons:slack.icns"
	set theApp to alias "Macintosh HD:Applications:Slack.app"
	
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:picFile
	set ws to current application's NSWorkspace's sharedWorkspace()
	ws's setIcon:theImage forFile:(POSIX path of theApp) options:0
end main

I assumed I was implying that by pasting so obvious a fix, that it didn’t work. However, given my interactions with others online (and my parents), I realize I should have explicitly stated that I did compile and I still could not save and got the same error.

Be that as it may, a user is allowed to change an icon of any app (even though it doesn’t belong to the user) via copy/paste in the get-info window. Thus, I can think of a dead-simple method just off the top of my head of changing the appearance in the dock: quit and re-launch the app via applescript. So apparently, it can.

I know it’s a compromise solution and is not technically what I asked, but if you consider what I’ve indicated thus far, it effectively solves the problem:

  1. I’m running the code to change the dock icon “via webhook based on my location (using geofences and iBeacons)” - so I’m not sitting at my desk and using the app when it runs, thus I’m not actively using the app and a re-launch would not likely interrupt anything important.
  2. I already have a (currently) working method that involves killing the dock with the unfortunate side-effect of eliminating other app badges - so I’m willing to consider somewhat drastic solutions.
  3. I’ve complained that my current solution clears other apps’ badges.

Thus, quitting and restarting the Slack app, while still drastic and not as satisfying a solution programmatically, is an acceptable solution. I didn’t think of it until now, but the discussion here gave me the inspiration. Thanks guys.

I appreciate the help you have offered. I definitely would not have figured out the portion of the solution you guys provided on my own.

I would still be interested if anyone were to discover a better solution of updating a dock icon’s appearance without re-launching it.

Sorry, I wasn’t trying to be smart – it’s just that I’ve never seen it not work. But I have seen Script Editor’s autosave get itself tied in a knot because of pointers, so perhaps that’s what happened.

IAC, that really is the fix. Did you make a small change to force re-compiling?

I created a new script, saved it (empty), then pasted the code from the script that wouldn’t save. Then I could save it. I don’t recall whether I compiled it first. I probably did. I usually do. I’m not aware of anything being different between the 2 scripts.

Sometimes, Text Edit won’t let me save documents, but the error in those cases is a permissions issue (even though there doesn’t appear to be a problem with permissions, so I’m rather used to non-sensical errors from macOS). In those cases, I have to copy out the text and paste it somewhere, then restart Text Edit.

I learned a bit more. It wasn’t as straight-forward as I initially thought.

Apparently, manually quitting & restarting the app via the dock-icon after running the applescript to update the changed icon works to update the icon in the dock, but quitting the app and relaunching it via applescript, such as:

tell application "Slack"
    quit
    delay 2
    activate
end tell

does not. Nor does it update the icon in the dock if I change the icon via script, command-tab to the Slack app, quit the app using command-q, and then re-launch it from spotlight.

Apparently, the launch of the app must be via the dock in order for the app icon to update.

This is the AppleScript that ended up accomplishing the end result. I created it by recording the launch of Slack (via the dock) in automator and then dragging the action into the workflow and copying out the applescript into the script that changes the app icon.

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

tell application "Slack" to quit

set picFile to alias "Macintosh HD:Users:username:.slack_icons:inoffice.icns"
set theApp to alias "Macintosh HD:Applications:Slack.app"

set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:picFile
set ws to current application's NSWorkspace's sharedWorkspace()
ws's setIcon:theImage forFile:(POSIX path of theApp) options:0

delay 1

set timeoutSeconds to 2.0
set uiScript to "click UI Element \"Slack\" of list 1 of application process \"Dock\""
my doWithTimeout(uiScript, timeoutSeconds)

on doWithTimeout(uiScript, timeoutSeconds)
	set endDate to (current date) + timeoutSeconds
	repeat
		try
			run script "tell application \"System Events\"
" & uiScript & "
end tell"
			exit repeat
		on error errorMessage
			if ((current date) > endDate) then
				error "Can not " & uiScript
			end if
		end try
	end repeat
end doWithTimeout

And this accomplished the dock icon update without having to kill the dock, delete its icon cache, and clear out any other apps’ badges (i.e. the unread Mail badge remains).