Debug and fix Swift macOS menubar app issues including code signing crashes, MainActor isolation errors, and app termination. Use when the client app won't launch, crashes on startup, shows code signature errors, or has threading/concurrency issues.
When the Alfred macOS client (client-prod) isn't working, follow this systematic debugging approach.
ls -t ~/Library/Logs/DiagnosticReports/Alfred*ps aux | grep Alfred | grep -v grep/path/to/Alfred.app/Contents/MacOS/Alfred 2>&1codesign -vvv /path/to/Alfred.appSymptoms:
"signal":"SIGKILL (Code Signature Invalid)"CODESIGNINGInvalid PageFix - Add required entitlements to Alfred.entitlements:
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
Fix - Fresh bundle ID (avoid cache issues):
# In build-app.sh, change:
BUNDLE_ID="com.bicyclelabs.alfred3-dev" # Use unique ID
Fix - Remove quarantine and re-sign:
xattr -cr /path/to/Alfred.app
codesign --force --deep --sign - --entitlements Alfred.entitlements /path/to/Alfred.app
Symptoms:
cannot be called from outside of the actorFix - Wrap calls in MainActor context:
// From async context:
await MainActor.run {
UserSession.shared.clearUser()
}
// From sync context:
Task { @MainActor in
UserSession.shared.clearUser()
}
Common files needing fixes:
TalkerService.swift - UserSession calls in actor methodsDeviceAuthService.swift - UserSession calls in sync methodsAlfredApp.swift - Notification observer closuresSymptoms:
👋 Alfred app terminatingFix - Add applicationShouldTerminateAfterLastWindowClosed:
// In AppDelegate
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return false // CRITICAL for menubar apps
}
Fix - Mark AppDelegate as @MainActor:
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
// ...
}
Symptoms:
Fix - Verify framework paths:
# Check what the executable expects
otool -L /path/to/Alfred.app/Contents/MacOS/Alfred
# Fix paths if needed
install_name_tool -add_rpath "@executable_path/../Frameworks" /path/to/Alfred.app/Contents/MacOS/Alfred
install_name_tool -change "@rpath/whisper.framework/Versions/A/whisper" "@executable_path/../Frameworks/whisper.framework/Versions/A/whisper" /path/to/Alfred.app/Contents/MacOS/Alfred
The whisper.framework has a non-standard layout. Sign in this order:
# 1. Sign dylibs in Libraries/ first
for dylib in Alfred.app/Contents/Frameworks/whisper.framework/Versions/A/Libraries/*.dylib; do
codesign --force --sign - "$dylib"
done
# 2. Sign the versioned binary
codesign --force --sign - Alfred.app/Contents/Frameworks/whisper.framework/Versions/A/whisper
# 3. Sign the framework bundle
codesign --force --sign - Alfred.app/Contents/Frameworks/whisper.framework
# 4. Sign Sparkle
codesign --force --deep --sign - Alfred.app/Contents/Frameworks/Sparkle.framework
# 5. Sign the app
codesign --force --deep --sign - --entitlements Alfred.entitlements Alfred.app
When things aren't working, do a completely fresh build:
# 1. Clean all caches
rm -rf client-prod/.build
rm -rf client-prod/.swiftpm
rm -rf client-prod/dist
rm -rf ~/Library/Developer/Xcode/DerivedData/*Alfred*
rm -rf ~/Library/Caches/com.bicyclelabs.alfred*
# 2. Update bundle ID to avoid stale cache
# Edit scripts/build-app.sh: BUNDLE_ID="com.bicyclelabs.alfred-fresh"
# 3. Build fresh
cd client-prod
bash scripts/build-app.sh
# 4. Remove quarantine
xattr -cr dist/Alfred.app
# 5. Launch
open dist/Alfred.app
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
| File | Purpose |
|---|---|
client-prod/Alfred.entitlements | Code signing entitlements |
client-prod/scripts/build-app.sh | Build script with bundle ID |
client-prod/AppKitUI/AlfredApp.swift | App lifecycle, StatusBar |
client-prod/Bridge/UserSession.swift | Thread-safe session state |
client-prod/Bridge/TalkerService.swift | Server communication |
client-prod/Bridge/DeviceAuthService.swift | OAuth flow |
# Check signature
codesign -vvv /path/to/Alfred.app
# Check Gatekeeper (will fail for ad-hoc, that's OK)
spctl -a -vvv /path/to/Alfred.app
# Check if running
ps aux | grep Alfred | grep -v grep
# View crash reports
ls -t ~/Library/Logs/DiagnosticReports/Alfred* | head -5
# Read latest crash
cat $(ls -t ~/Library/Logs/DiagnosticReports/Alfred* | head -1)