Environment
- Hardware: MacBook Pro, Apple Silicon (M1 Max)
- OS: macOS 15.7.x
- Build tested: Official release v1.1.0 downloaded from GitHub (DMG / app as distributed at the time). The source code that I downloaded said it was v1.0.0, which is earlier than the downloadable release build (v1.1.0). So no wonder it needed Sparkle again.
Summary
The distributed application did not stay running after double‑click: it never appeared in the menu bar, and did not show as a normal running app in Activity Monitor (no usable long‑lived process from a user’s perspective). Because source code is available, I built and debugged locally and got a working .app. Below is a concise report of what was wrong and what changed so it runs, in case it helps the next release and others on similar setups.
This is my first time preparing a fork / PR against an upstream project; I’m sharing this in the spirit of debugging and community contribution, not criticism of the project.
Symptoms
- Launch failure: Opening the released app did not produce a stable menu bar extra; the experience was “nothing useful happens” or an error dialog, not a working PowerMate menu.
- No visible process: In Activity Monitor, the app did not appear as a normal running app to interact with (depending on failure mode, either no process or a very short‑lived one).
- Menu bar: No PowerMate status item, so none of the documented menu‑driven controls were reachable from the UI.
Root causes identified (local debugging)
1. Sparkle framework not packaged in the .app
The executable links Sparkle as @rpath/Sparkle.framework/... (with @loader_path in the RPATH). The SwiftPM build places Sparkle.framework next to the binary under .build/release/, but the packaging script only copied the main binary into Contents/MacOS/ and did not copy Sparkle.framework.
Result: dyld failed at launch with Library not loaded: Sparkle — the process exits immediately before any UI. That matches “doesn’t show in Activity Monitor” / “doesn’t appear in the menu bar” from an end‑user perspective.
Fix: Copy Sparkle.framework into the app bundle next to the executable (e.g. Contents/MacOS/Sparkle.framework) so it matches the loader path the linker already uses.
2. Incorrect private API signature for DisplayServicesGetBrightness
BrightnessController probes displays during initialization and calls the private DisplayServices DisplayServicesGetBrightness symbol loaded via dlsym.
The implementation treated the function as (CGDirectDisplayID) -> Float, but the actual C API uses an out‑parameter: (displayID, &brightness) -> Bool.
Result: EXC_BAD_ACCESS inside DisplayServicesGetBrightness during BrightnessController.init() → instant crash on launch (seen in Crash Reports as a fault in DisplayServices + BrightnessController.detectMethod).
Fix: Use the correct calling convention: pass a UnsafeMutablePointer for brightness and handle the Boolean return; wrap reads in a small helper used everywhere brightness is read for DisplayServices.
3. Sparkle updater initialized with placeholder Info.plist keys
Info.plist contained SUPublicEDKey set to a placeholder string (e.g. REPLACE_WITH_YOUR_EDDSA_PUBLIC_KEY) while SPUStandardUpdaterController(startingUpdater: true, ...) still ran.
Result: Sparkle showed “Unable to Check For Updates” / “The updater failed to start” on launch — a modal that blocks the rest of startup until dismissed and can make the app feel “broken” or “not in the menu bar” until the dialog is cleared.
Fix: Do not start the standard Sparkle updater unless SUFeedURL and SUPublicEDKey look like a real release configuration (non‑placeholder, reasonable length, HTTPS feed). Otherwise skip Sparkle and leave “Check for Updates” disabled until keys are configured for a signed release pipeline.
4. (UX / discoverability) Crowded or notched menu bar
Even with a healthy process, macOS can omit status items when the menu bar is full (especially near the notch). That is environmental, not necessarily a bug in the app.
Mitigation we used locally: Regular activation policy + Dock icon + minimal App menu entries (e.g. “Open Controls & Menu…”, Custom settings, About, Quit) so the app is reachable even when the extra is off‑screen. This is optional product‑wise but greatly helps support.
What “works” now (high level)
- dyld finds Sparkle → process starts.
- Brightness probing uses DisplayServices correctly → no early crash in BrightnessController.
- Sparkle does not block startup with a bad configuration → no spurious updater failure dialog for local/unsigned builds.
- Optional: Dock + App menu makes the full status menu reachable if the icon is hidden by the system.
Offer
I can open a pull request with:
- Packaging: embed Sparkle.framework in the DMG / .app script.
- BrightnessController: correct DisplayServicesGetBrightness usage.
- Sparkle: gate updater on valid feed + EdDSA key (or document required keys for release builds).
- Optional: Dock / minimal App menu for discoverability.
Thanks
Thanks for maintaining PowerMateReborn and publishing source — that made local debugging possible. I’m glad to align changes with your preferred workflow and release process.
You can shorten sections for a shorter issue, or attach crash log excerpts and Console / dyld lines if you want maximum signal for the maintainer.
Environment
Summary
The distributed application did not stay running after double‑click: it never appeared in the menu bar, and did not show as a normal running app in Activity Monitor (no usable long‑lived process from a user’s perspective). Because source code is available, I built and debugged locally and got a working .app. Below is a concise report of what was wrong and what changed so it runs, in case it helps the next release and others on similar setups.
This is my first time preparing a fork / PR against an upstream project; I’m sharing this in the spirit of debugging and community contribution, not criticism of the project.
Symptoms
Root causes identified (local debugging)
1. Sparkle framework not packaged in the .app
The executable links Sparkle as @rpath/Sparkle.framework/... (with @loader_path in the RPATH). The SwiftPM build places Sparkle.framework next to the binary under .build/release/, but the packaging script only copied the main binary into Contents/MacOS/ and did not copy Sparkle.framework.
Result: dyld failed at launch with Library not loaded: Sparkle — the process exits immediately before any UI. That matches “doesn’t show in Activity Monitor” / “doesn’t appear in the menu bar” from an end‑user perspective.
Fix: Copy Sparkle.framework into the app bundle next to the executable (e.g. Contents/MacOS/Sparkle.framework) so it matches the loader path the linker already uses.
2. Incorrect private API signature for DisplayServicesGetBrightness
BrightnessController probes displays during initialization and calls the private DisplayServices DisplayServicesGetBrightness symbol loaded via dlsym.
The implementation treated the function as (CGDirectDisplayID) -> Float, but the actual C API uses an out‑parameter: (displayID, &brightness) -> Bool.
Result: EXC_BAD_ACCESS inside DisplayServicesGetBrightness during BrightnessController.init() → instant crash on launch (seen in Crash Reports as a fault in DisplayServices + BrightnessController.detectMethod).
Fix: Use the correct calling convention: pass a UnsafeMutablePointer for brightness and handle the Boolean return; wrap reads in a small helper used everywhere brightness is read for DisplayServices.
3. Sparkle updater initialized with placeholder Info.plist keys
Info.plist contained SUPublicEDKey set to a placeholder string (e.g. REPLACE_WITH_YOUR_EDDSA_PUBLIC_KEY) while SPUStandardUpdaterController(startingUpdater: true, ...) still ran.
Result: Sparkle showed “Unable to Check For Updates” / “The updater failed to start” on launch — a modal that blocks the rest of startup until dismissed and can make the app feel “broken” or “not in the menu bar” until the dialog is cleared.
Fix: Do not start the standard Sparkle updater unless SUFeedURL and SUPublicEDKey look like a real release configuration (non‑placeholder, reasonable length, HTTPS feed). Otherwise skip Sparkle and leave “Check for Updates” disabled until keys are configured for a signed release pipeline.
4. (UX / discoverability) Crowded or notched menu bar
Even with a healthy process, macOS can omit status items when the menu bar is full (especially near the notch). That is environmental, not necessarily a bug in the app.
Mitigation we used locally: Regular activation policy + Dock icon + minimal App menu entries (e.g. “Open Controls & Menu…”, Custom settings, About, Quit) so the app is reachable even when the extra is off‑screen. This is optional product‑wise but greatly helps support.
What “works” now (high level)
Offer
I can open a pull request with:
Thanks
Thanks for maintaining PowerMateReborn and publishing source — that made local debugging possible. I’m glad to align changes with your preferred workflow and release process.
You can shorten sections for a shorter issue, or attach crash log excerpts and Console / dyld lines if you want maximum signal for the maintainer.