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

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