Changing Dock Icon for my Qt app

Hello,

I'm trying to make a white-Label sort of thing for my app, that is: a script runs before the app launches, sets a certain LaunchAgent command that sets and environment variable, and based on that variable's value tha main app's icon changes to a certain logo (change only happens in the dock because changing the icon on disk breaks the signature)

When the app launches it takes a noticeable time until the dock icon changes to what I want, so I worked around that by setting the app's plist property to hide the dock icon and then when the app is launched I call an objc++ function to display the icon in the dock again (this time it displays as the new icon) The showing happens through [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

The problem happens when I try to close the app, it returns back to the old logo before closing which is what I want to prevent. I tried hiding the app dock icon before closing but even the hiding itself changes the icon before hiding The hiding happens through [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];

My goal is that the main app icon doesn't appear to the user through the dock, and that the icon that is only visible is the other one that changes during runtime

The reason for this is that I have an app that should be visible differently depending on an environment variable that I set using an installer app. The app is the same for all users with very minor UI adjustments depending on that env variable's value. So instead of creating different versions of the app I'd like to have just 1 version that adjusts differently depending on the env variable's value. Somehow this is the only step left to have a smooth experience

Feel free to ask more clarification questions I'd be happy to help Thank you

Answered by DTS Engineer in 872049022

It’s better to reply as a reply, rather than in the comments; see Quinn’s Top Ten DevForums Tips for this and other titbits.

Thanks for those answers.

My advice here is that you change tack. Rather than try to set this at runtime based on an environment variable, have your installer install an app with the right icon.

That doesn’t mean that you need N installers for your N icons. There are a couple of ways that you can customise the icon on the fly.

The first is to have a post-install script that applies a custom icon to your app. This has one key advantage: It’s easy to implement (-: Specifically, setting a custom icon doesn’t affect the app’s code signature.

However, I’m not sure it’s actually the right option for you, because of the issues you raised in your other thread, Changing the menu bar display name. There’s no way to set a custom app name like this.

Honestly, I can’t think of a good way to set the app name at all. macOS expects apps to be static artefacts. While you can change some of this stuff at runtime — like the icon, for example — that’s the exception rather than the rule.

To get this level of customisation you need to ship multiple apps. However, that doesn’t mean you need to ship multiple installers. You can have a single installer that holds all the apps, deduplicating stuff to minimise the package size, and then install the right one based on the user’s choice.

However, this is no longer easy. There are two tricky issues:

The solution is:

  1. Build, sign, and notarise the app N times.
  2. Use one of the app’s as the base for your installer.
  3. For the remaining N - 1 apps, diff it against that app and store the diffs.
  4. Have your installer install the base app and then the appropriate diff.

The diffs should be quite small, being limited to:

  • The Info.plist
  • The icon [1]
  • The main executable [2]
  • Some files in Contents/_CodeSignature that hold various bits of code signature

The end result of this is that the installed app has a valid code signature, a unique main executable UUID, and a custom app name and icon, which should be sufficient to avoid all the weird edge cases you’re seeing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Another option is to include all the icons in the base app and then just change the Info.plist value that references the icon. This should result in a smaller diff.

[1] If the main executable is large, you can reduce it by moving the bulk of your app’s code to a framework (or dynamic library) nested in the app’s bundle and then have the main executable be a stub that just calls through to that framework.

I think you’ve started off on the wrong path and now you’re starting to realise just how wrong it is )-:

I’d like to clarify your requirements here. You only want to customise the app’s icon. That is:

  • You’re using -setActivationPolicy: as a workaround for the fact that you’re setting the icon programmatically.
  • Not because your app pops in and out of the background.

Is that right?

Also, where are you getting these icons from? Is it a small set that’s built in to your app? Or something more complex?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It’s better to reply as a reply, rather than in the comments; see Quinn’s Top Ten DevForums Tips for this and other titbits.

Thanks for those answers.

My advice here is that you change tack. Rather than try to set this at runtime based on an environment variable, have your installer install an app with the right icon.

That doesn’t mean that you need N installers for your N icons. There are a couple of ways that you can customise the icon on the fly.

The first is to have a post-install script that applies a custom icon to your app. This has one key advantage: It’s easy to implement (-: Specifically, setting a custom icon doesn’t affect the app’s code signature.

However, I’m not sure it’s actually the right option for you, because of the issues you raised in your other thread, Changing the menu bar display name. There’s no way to set a custom app name like this.

Honestly, I can’t think of a good way to set the app name at all. macOS expects apps to be static artefacts. While you can change some of this stuff at runtime — like the icon, for example — that’s the exception rather than the rule.

To get this level of customisation you need to ship multiple apps. However, that doesn’t mean you need to ship multiple installers. You can have a single installer that holds all the apps, deduplicating stuff to minimise the package size, and then install the right one based on the user’s choice.

However, this is no longer easy. There are two tricky issues:

The solution is:

  1. Build, sign, and notarise the app N times.
  2. Use one of the app’s as the base for your installer.
  3. For the remaining N - 1 apps, diff it against that app and store the diffs.
  4. Have your installer install the base app and then the appropriate diff.

The diffs should be quite small, being limited to:

  • The Info.plist
  • The icon [1]
  • The main executable [2]
  • Some files in Contents/_CodeSignature that hold various bits of code signature

The end result of this is that the installed app has a valid code signature, a unique main executable UUID, and a custom app name and icon, which should be sufficient to avoid all the weird edge cases you’re seeing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Another option is to include all the icons in the base app and then just change the Info.plist value that references the icon. This should result in a smaller diff.

[1] If the main executable is large, you can reduce it by moving the bulk of your app’s code to a framework (or dynamic library) nested in the app’s bundle and then have the main executable be a stub that just calls through to that framework.

Thank you for the reply it's really helpful!

For the name in the menu bar issue I'll probably stick to a name (at compile time) that can be generic enough for all other versions of the app so as to avoid all of that

For the icon issue I noticed you said:

The first is to have a post-install script that applies a custom icon to your app. This has one key advantage: It’s easy to implement (-: Specifically, setting a custom icon doesn’t affect the app’s code signature.

How exactly? The reason I'm doing these Dock icon change shenanigans is because I tried changing the icon of the app after installation and it somehow breaks the signature. Do I change it from the app itself after it launches? or do I change it from the installer app ? The installer app downloads the main app and then extracts it to an appropriate location

Accepted Answer
I tried changing the icon of the app after installation and it somehow breaks the signature.

Indeed. And that’s not a mystery at all. In general, changing content within an app’s bundle will break the seal on the code signature. If you’re curious how that all works, see TN3126 Inside Code Signing: Hashes.

But a custom icon is special. You can apply a custom icon to an app without breaking its signature. To see this in action, go to Finder, select your app, choose File > Get Info, select the icon, and paste in an image.

I regularly do this with older Xcode versions. For example, my main Xcode is currently Xcode 26.2, but I have Xcode 16.4 around for testing. I apply a custom icon to that older Xcode to help head of the confusion that inevitably occurs when you have multiple versions of Xcode running.

If you dig into the on-disk results of this, you’ll see that it makes two changes:

  1. It sets the kHasCustomIcon bit:

    % GetFileInfo "Xcode 16.4.app"
    …
    attributes: avbstClinmedz
                     ^ kHasCustomIcon
    
  2. It stores the icon in an invisible the Icon\r file:

    % ls "Xcode 16.4.app" | cat
    Contents
    Icon
    % GetFileInfo "Xcode 16.4.app/Icon^M" 
    …
    attributes: aVbstclinmEdz
                 ^ kIsInvisible
    

IMPORTANT That file name ends with a CR. To see it:

% ls "Xcode 16.4.app" | xxd       
00000000: 436f 6e74 656e 7473 0a49 636f 6e0d 0a    Contents.Icon..
                                          ^^ CR                                       

This file contains an 'icns' (-16455) resource:

% DeRez "Xcode 16.4.app/Icon^M" 
data 'icns' (-16455) {
	$"6963 6E73 001A E15E 6963 3132 0000 238A"            /* icns..?^ic12..#? */
    …
};

Fortunately, you don’t need to mess around with these details in your post-install script because you can just prepackage a bunch of Icon\r files and copy the right one into place.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I'm replying to let you know that you are an absolute legend :D this is exactly what I needed. I've been implementing a post-install icon change from the custom installer to the app itself and it works so smoothly. It's MUCH better than all the hacky stuff I was trying to do earlier. Thank you so much, I can now sleep with a clear mind

Changing Dock Icon for my Qt app
 
 
Q