I have a macOS app with a WidgetKit widget extension, distributed two ways: a notarised Developer ID download, and a Mac App Store build (same bundle identifiers, same team, different signature). When a user replaces the Developer ID copy in /Applications with the store-signed copy, the desktop widget draws only the grey placeholder and never recovers. The app itself runs normally.
What I saw (8 October 2026, macOS 27):
An iMac had the Developer ID build 47 (version 1.6.3) in /Applications with the widget on the desktop.
I installed build 48 (version 1.6.4), signed for the store, from TestFlight. It replaced the Developer ID copy in /Applications.
The app worked. The large widget showed grey placeholder bars and no data, for at least five minutes.
killall chronod in Terminal brought the widget back straight away.
On a MacBook, updating from one TestFlight build to the next (builds 46, 47, 48) redrew the widget each time. So it is the change from a Developer ID signature to a store signature, not an ordinary update.
What I think is happening, and would like confirmed or corrected: the widget process that was started from the Developer ID copy keeps running after the app is replaced, and the system refuses what it draws (or never asks it for a new timeline), while the widget gallery and desktop keep showing that process's placeholder.
What I already do:
The Developer ID build ships a small helper outside the sandbox that ends the old widget process after an update. The Mac App Store build cannot carry that helper.
The widget extension compares the build number in its bundle's Info.plist on disk with the one it started with, and exits if they differ. This ran in the case above (the build numbers were 47 and 48) and did not clear the placeholder. I suspect the check only runs when the system asks the widget for a snapshot or timeline, and the system had stopped asking.
I call WidgetCenter.shared.reloadAllTimelines() after the app writes new data.
Questions:
Is there a supported way for a sandboxed app (or its widget extension) to make the system replace or restart a widget process that belongs to an earlier, differently signed copy of the same app? Without a Terminal command, and without a helper outside the sandbox.
Is it expected that chronod keeps the old extension process after the containing app is replaced by a copy with a different signing identity, and that the widget then stays on the placeholder?
Is there a log subsystem or predicate that shows why the old extension process was kept, or what the system refused?
Thank you.
0
0
140