Skip to content

Fix SetApplicationIconImage: resolve NSImage after AppKit is loaded - #18

Merged
tannevaled merged 2 commits into
mainfrom
fix-setapplicationiconimage-load-order
Sep 9, 2026
Merged

tannevaled merged 2 commits into
mainfrom
fix-setapplicationiconimage-load-order

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

SetApplicationIconImage (#17) called ClassID("NSData")/ClassID("NSImage") before ever calling App() — the only thing that triggers AppKit's lazy load (appKitOnce.Do). For a caller where this is the first AppKit-touching call in the process — exactly the case it exists for, a menu-bar app's own startup, before any window or tray call — NSImage resolves to a nil class, alloc/initWithData: are Objective-C's well-defined "message to nil" no-op, and img == 0 silently short-circuits the whole function. App()'s own doc comment already describes this exact failure mode for a caller who forgets to load AppKit first; this function made the same mistake internally.

The on-device test suite didn't catch it: objc_darwin_test.go's own init() pre-loads Foundation+AppKit for every test in the shared binary, which is precisely the condition that hides this bug — no test in that binary is ever the "first AppKit call" in its process by construction.

Found live: go-aiquota/tray's real menu-bar app called SetApplicationIconImage as its first-ever AppKit interaction on every real launch, and the Dock icon never appeared. Confirmed root cause with a standalone single-purpose program (not go test, which structurally can't reproduce "first call in the process").

Fix: call App() first, use its return value for the final assignment.

Test plan

  • go build ./..., CGO_ENABLED=0 go vet ./..., gofmt -l . clean
  • CGO_ENABLED=0 go test ./... green on darwin
  • Verified live with a standalone program calling only SetApplicationIconImage as the first AppKit call in the process — nil image before the fix, correct 1024×1024 image after

🤖 Generated with Claude Code

tannevaled and others added 2 commits September 9, 2026 15:50
SetApplicationIconImage called ClassID("NSData")/ClassID("NSImage")
BEFORE ever calling App() — the only thing that triggers AppKit's
lazy load (appKitOnce.Do). For a caller where this is the FIRST
AppKit-touching call in the process (exactly the case it exists for:
a menu-bar app's own startup, before any window or tray call), NSImage
resolves to a nil class, alloc/initWithData: are Objective-C's
well-defined "message to nil" no-op, and img == 0 silently short-
circuits the whole function. App()'s own doc comment already
describes this exact failure mode for a caller who forgets to load
AppKit first — this function made the same mistake internally.

The on-device test suite didn't catch it: objc_darwin_test.go's own
init() pre-loads Foundation+AppKit for every test in the binary,
which is precisely the condition that hides this bug. Found by
building a real, single-purpose standalone program that calls
SetApplicationIconImage as its first-ever AppKit interaction (go
test's shared test binary can't reproduce that "first call" state
by construction) — confirmed via go-aiquota/tray's real menu-bar app,
which hit this exact ordering on every real launch.

Fix: call App() first, use its return for the final assignment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit cad827f into main Sep 9, 2026
3 checks passed
@tannevaled
tannevaled deleted the fix-setapplicationiconimage-load-order branch September 9, 2026 14:01
tannevaled added a commit to go-aiquota/tray that referenced this pull request Sep 9, 2026
Live user request: "il faudrait avoir une icon dock pour la fenetre
d'history" — this otherwise menu-bar-only (accessory-policy) process
already switches to Regular activation policy while a real window
(the history chart) is open, per go-widgets/tray's own
RunAppLoop/window.Open dance — so the Dock tile itself already
appears, but an unsigned binary with no Info.plist icon resource got
no image for it, not merely a generic one.

Embeds the org's own brand PNG (go-aiquota/brand/png/color/1024) and
sets it via go-macos/objc.SetApplicationIconImage once at startup.

Needed a real fix upstream first (go-macos/objc#18, v0.10.1):
SetApplicationIconImage resolved NSImage before ever loading AppKit,
so calling it as the FIRST AppKit interaction in a process — exactly
this app's own startup — silently no-opped. Confirmed root cause with
a standalone diagnostic (go test's shared test binary structurally
can't reproduce "first AppKit call in the process").

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 9, 2026
)

Live bug report, after the icon finally started appearing (#18): it
rendered visibly larger than every sibling Dock tile. An NSImage built
via alloc/initWithData: takes its .size from the bitmap's own PIXEL
count when no DPI metadata says otherwise — a crisp 1024x1024 source
PNG (what a real Retina-ready brand asset actually is) reported an
image AppKit's layout treats as 1024 POINTS across, not a normal app
icon's size.

go-widgets/tray already solved the identical problem for its own
menu-bar/menu-row icons (nsImageFromPNG) — same fix here: an explicit
setSize: call at dockIconPoints (128, macOS's own "large icon"
convention), preserving aspect ratio. Turns the extra pixels into
resolution instead of dimensions.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant