Skip to content

Commit 2ec37ee

Browse files
Xpycodeclaude
andcommitted
v1.0.1: cycle FinderInfo kHasCustomIcon flag on every apply
Force a clear -> set transition of the kHasCustomIcon FinderInfo flag on every apply (was previously just |= the bit, which on overwrites wrote the same byte and didn't trigger any state change Finder could observe). Empirically does not, on its own, force Finder to refresh stale cached volume icons -- which was the original motivation, ultimately addressed by removing the zoom slider in 1eaa148 (the surface that exposed the cache problem most often). But the cycle is correct hygiene: first applies degrade to a plain set, overwrites get a clean state transition, other FinderInfo bytes (colour labels, etc.) are preserved. Kept in reserve for any future macOS release where this turns out to be the missing piece. Documented in decisions.md ("Finder cache invalidation, round 3"). NOTE: This change was already present in the v1.0.1 notarized .app exported earlier (built locally after the edit, before being committed). Committing now so the repo matches the shipped binary before the tag moves to point here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1eaa148 commit 2ec37ee

1 file changed

Lines changed: 23 additions & 2 deletions

File tree

01_Project/Sigil/Services/IconApplier.swift

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,15 @@ actor IconApplier {
9292
throw Error.underlying(error)
9393
}
9494

95-
// Step 2: read-modify-write FinderInfo to set byte 8 = 0x04.
95+
// Step 2: cycle the FinderInfo `kHasCustomIcon` flag (clear → set) so
96+
// Finder sees a state transition, not just a same-bit re-write.
97+
// First applies (flag not yet set) degrade to a plain set; overwrites
98+
// (flag already set from a previous apply) get the toggle, which
99+
// empirically punches through Finder's icon-services cache when
100+
// `noteFileSystemChanged` alone wasn't enough. See decision log
101+
// 2026-05-04 ("Finder cache invalidation, round 3").
96102
do {
97-
try Self.setCustomIconFlag(on: volumeURL)
103+
try Self.cycleCustomIconFlag(on: volumeURL)
98104
} catch {
99105
// Roll back the orphan icon file — otherwise we'd leave garbage.
100106
try? fm.removeItem(at: iconURL)
@@ -154,6 +160,21 @@ actor IconApplier {
154160

155161
// MARK: - Private helpers
156162

163+
/// Force a `kHasCustomIcon` state transition by clearing then setting
164+
/// the flag. Bypasses Finder's icon-services cache, which on overwrites
165+
/// otherwise sees "flag still set, no change" and serves the stale
166+
/// cached icon.
167+
///
168+
/// Safe with respect to other FinderInfo bytes (Finder colour labels
169+
/// etc.): the underlying `clearCustomIconFlag` only touches byte 8 and
170+
/// preserves the rest of the 32-byte blob; if the rest is all-zero, it
171+
/// removes the xattr entirely (cleanest "fresh state" signal to
172+
/// Finder), and `setCustomIconFlag` re-creates it with just byte 8 set.
173+
private static func cycleCustomIconFlag(on volumeURL: URL) throws {
174+
try clearCustomIconFlag(on: volumeURL)
175+
try setCustomIconFlag(on: volumeURL)
176+
}
177+
157178
private static func setCustomIconFlag(on volumeURL: URL) throws {
158179
var info = (try XAttr.get(name: finderInfoKey, from: volumeURL.path)) ?? Data()
159180
if info.count < finderInfoLength {

0 commit comments

Comments
 (0)