Swift macOS Client Debugging Guide
When the Alfred macOS client (client-prod) isn't working, follow this systematic debugging approach.
Quick Diagnosis Checklist
- •Check crash reports:
ls -t ~/Library/Logs/DiagnosticReports/Alfred* - •Check if running:
ps aux | grep Alfred | grep -v grep - •Run directly:
/path/to/Alfred.app/Contents/MacOS/Alfred 2>&1 - •Verify signature:
codesign -vvv /path/to/Alfred.app
Common Issues & Fixes
1. Code Signature Invalid (SIGKILL)
Symptoms:
- •App crashes immediately on launch
- •Crash report shows:
"signal":"SIGKILL (Code Signature Invalid)" - •Termination namespace:
CODESIGNING, indicator:Invalid Page
Fix - Add required entitlements to Alfred.entitlements:
xml
<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):
bash
# In build-app.sh, change: BUNDLE_ID="com.bicyclelabs.alfred3-dev" # Use unique ID
Fix - Remove quarantine and re-sign:
bash
xattr -cr /path/to/Alfred.app codesign --force --deep --sign - --entitlements Alfred.entitlements /path/to/Alfred.app
2. MainActor Isolation Errors
Symptoms:
- •Build errors:
cannot be called from outside of the actor - •Runtime crashes with threading violations
Fix - Wrap calls in MainActor context:
swift
// 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 methods - •
DeviceAuthService.swift- UserSession calls in sync methods - •
AlfredApp.swift- Notification observer closures
3. App Terminates Immediately
Symptoms:
- •App starts, logs appear, then
👋 Alfred app terminating - •No crash report generated
Fix - Add applicationShouldTerminateAfterLastWindowClosed:
swift
// In AppDelegate
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return false // CRITICAL for menubar apps
}
Fix - Mark AppDelegate as @MainActor:
swift
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
// ...
}
4. Framework Loading Failures
Symptoms:
- •Crashes in dyld during startup
- •"Library not found" errors
Fix - Verify framework paths:
bash
# 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
5. Whisper Framework Signing
The whisper.framework has a non-standard layout. Sign in this order:
bash
# 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
Clean Build Process
When things aren't working, do a completely fresh build:
bash
# 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
Required Entitlements for Ad-Hoc Signing
xml
<?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>
Key Files
| 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 |
Verification Commands
bash
# 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)