Change the icon of an AppleScript app after launching?

I use the same AppleScript applet to perform differerent actions, depending on the string in its name. I would like the applet to display a different icon in addition to performing different functions.

An old MacScripter thread suggests that this was possible in the AppleScript Studio days:

Is this possible with current AppleScript? I can’t see anything obvious, but I only know 0.05% of AppleScriptObjC (or less).

Assuming you have an image in the app’s bundle called SomeImage.xyz (where xyz is a valid image file extension), something like this (untested) should work:

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

set theImage to current application's NSImage's imageNamed:"SomeImage.xyz"
current application's NSApp's setApplicationIconImage:theImage -- edited

Setting the icon image to missing value will restore the default image.

Thank you! That works perfectly.

At first, I was frustrated by getting this error

-[ScriptDebuggerApplication setApplicationIcomImage:]: unrecognized selector sent to instance 0x1256074f0

and then I saw that the example code had the mistyped string IcomImage instead of IconImage. All’s well.

1 Like

Interesting … how would I use this with album artwork data from Music instead of a file? (Disclaimer: I’m a drunken chipmunk with a chainsaw when it comes to ASObjC. Someone’s likely to lose a limb.)

tell application "Music"
	
	set albumArt to artwork 1 of current track
	set albumArtData to data of albumArt
	
end tell

Saving it to disk just to reload it seems wasteful if it can be avoided. I gather that albumArtData needs to be converted to NSData somehow, but I’m stuck on that part.

I’ve been experimenting a bit with ChatGPT, and it came up with this code that looks like it’s heading in the right direction, but the first line won’t compile:

set albumArtData to current application's NSData's dataWithBytes:length:(albumArtData's length)
set albumArtImage to current application's NSImage's alloc()'s initWithData:albumArtData

current application's NSApplication's sharedApplication()'s setApplicationIconImage:albumArtImage

The error is Expected end of line, etc. but found “:”, objecting to the colon after dataWithBytes:length:.

Removing length: allows it to compile, but then it says it Can’t get length of «data tdta… when it runs.

Drunken chipmunk, now with AI. What could go wrong? :stuck_out_tongue_winking_eye:

ChatGPT is not a good idea. I have hardly found anything (as far as AppleScript is concerned) that works. If anything, the first line should look something like this:

set albumArtData to current application's NSData's dataWithBytes:buffer |length|:(buffer's |length|)

See: developer.apple.com

1 Like

I’m under no illusions. I’ve been highly skeptical about its usefulness for programming, so this is just me kicking the tires. It has helped me get my head around some concepts with applets and given me a scaffolding to build on, even if I had to fix some bugs in the code. Those were obvious, unlike this one.

In any event, I’d rather test its limitations before I bother people here.

I still have trouble at this point getting from the Cocoa documentation to working ASObjC code, what with Shane’s book being out of print. So I’m in drunken chipmunk mode until I stumble on something that does what I want, and that’s a pretty rare occurrence.

It’s failing with The variable buffer is not defined. Is that referencing a characteristic of albumArtData, or is there an actual buffer that I need to create first?

(If you think this is bad, you should see the violence I’ve been doing to CoreAudio. Somehow I keep borking the sound effects output on my machine, and every error message causes the screen to flash. Drives me nuts. Then I have to sudo killall coreaudiod to get it working again… Gonna be a while before I isolate the cause of that one.)

That method requires a pointer, so it won’t work with ASObjC. This used to work:

set aeDisc to current application's NSAppleEventDescriptor's alloc()'s initWithAEDescNoCopy:albumArtData
set albumArtData to current application's NSData's dataWithData:(aeDisc's |data|())

but the first line just seems to cause crashes here now.

I have a vague memory of some other workaround to get from AS data to NSData (other than BridgePlus), but I can’t remember it. I wonder if @NigelGarvey can give a hint.

Ayuh, I can confirm the crash. :stuck_out_tongue: Not as straightforward as I was hoping.

So is writing it to a file the path of least resistance?

I suspect that’s the case.

Put it on the clipboard and use ASObjC methods to retrieve it?

Yep, that would work.

I thought there was some way via a scring in UTF-8 form, but I may have my wires crossed.

I have it working with a file, so now I’m trying the clipboard. I think I’m OK on the AS side, but seem to be missing something at the other end.

With a file named cover.jpg copied to the clipboard, I tried this:

set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theItems to thePasteboard's pasteboardItems()
set albumArtNSImage to item 1 of theItems

That gives me one item in the form of <NSPasteboardItem: 0x133e93330>, but it doesn’t appear to be an image. Or at least SD isn’t displaying it as an image. Even text on the clipboard is coming back like <NSPasteboardItem: 0x133e93330>, so I must be doing it wrong.

Try this:

set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theImage to item 1 of (thePasteboard's readObjectsForClasses:{current application's NSImage} options:{})

1 Like

Bingo. Figured there was a bit more to it.

I may end up sticking with file operations anyway, because now I’m looking into ways to composite the artwork onto the official Apple Squircle, and that probably calls for something like ImageMagick. Which I’ve never touched before, so this should be fun.

You can do image compositing with NSImage (and/or NSImageRep). There should be code around here covering how to do it.

There’s also an NSImage method specifically for getting an image from the pasteboard:

set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theImage to current application's NSImage's alloc()'s initWithPasteboard:(thePasteboard)
1 Like