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

I just implemented a pair of bash functions to set a custom app icon badge to the slack app in the dock to indicate my slack status, which I intend to call via webhook based on my location (using geofences and iBeacons). I already update my slack status using this webhook strategy and I can use the same strategy to call these bash functions. Here’s my first version of the bash functions I experimented with:

[format]function slack_regular() {
cp ~/.slack_icons/slack.icns /Applications/Slack.app/Contents/Resources/slack.icns
touch /Applications/Slack.app
rm -f /var/folders///*/com.apple.dock.iconcache
killall Dock
}

function slack_inoffice() {
cp ~/.slack_icons/inoffice.icns /Applications/Slack.app/Contents/Resources/slack.icns
touch /Applications/Slack.app
rm -f /var/folders///*/com.apple.dock.iconcache
killall Dock
}[/format]

The bash functions work on the command line to update the slack app icon, but I don’t like how drastic it is to kill the dock every time, so I was wondering about how currently supported app badges work and wondering if I can take advantage of that existing badge update mechanism so that I can apply my custom image-based badges to the top-right corner of an app icon. …And if I can do it via applescript or some existing pre-made app.

The reason I’m doing this… I trigger the webhook calls via an iPhone app called Geofency, which can make webhook calls based on geofences and iBeacons, but my cell provider is cdma, so when I’m on a call, as I often am when I leave work, the geofence triggers fine but the webhook fails. I have it set to notify me upon error, but it never notifies me… so my slack status in those instances (or other unknown errors) doesn’t update. I just want to be able to see my status at a glance without clicking on slack to bring it to the front to inspect my status.

I used the emoji I apply to the status updates for the badge.

The apps you see in the Dock are displayed programmatically from data stored in a property list (.plist) at this path:

/Users/<your_user_name>/Library/Preferences/com.apple.Dock.plist

The only exception is the Finder.app. The Dock Tile presence and position for Finder in the Dock are hardcoded within the

/System/Library/CoreServices/Dock.app/Contents/MacOS/Dock

executable and the Dock Tile icons for Finder in the Dock are located in

/System/Library/CoreServices/Dock.app/Contents/Resources

, thus making this just a normal working part of the Dock.app. It is one of two Dock Tiles, the other being the Trash, in which the position on the Dock cannot be changed due to being hard coded within the Dock executable.

System retrieves icons via file-data property group of each item in dock plist. I would draw the following conclusion from this information: in order for the application icon to change, you need to change the reference to application icon in the info plist of the application bundle itself.

I would caution against editing the info.plist in any application bundle, as it risks breaking file and value references that allow the application to operate. Editing the icon file in the Resources folder of the application bundle is a safer option.

Neither are a good idea. The former is going to invalidate the code signature, and as checks get tighter in the OS, the latter will too. It risks making an app unusable.

Good to know. I presume this goes for editing any file resource in an app bundle, be it a sound file, a jpeg, or what not ? Basically, I’m interpreting this to mean “leave the entire contents of an app bundle alone,” is that fair to say ?

Exactly.

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.