AgentSkillsCN

swift-macos-client-debug

调试并修复Swift macOS菜单栏应用的问题,包括代码签名崩溃、MainActor隔离错误,以及应用终止问题。适用于客户端应用无法启动、启动时崩溃、显示代码签名错误,或存在线程/并发问题的场景。

SKILL.md
--- frontmatter
name: swift-macos-client-debug
description: 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.

Swift macOS Client Debugging Guide

When the Alfred macOS client (client-prod) isn't working, follow this systematic debugging approach.

Quick Diagnosis Checklist

  1. Check crash reports: ls -t ~/Library/Logs/DiagnosticReports/Alfred*
  2. Check if running: ps aux | grep Alfred | grep -v grep
  3. Run directly: /path/to/Alfred.app/Contents/MacOS/Alfred 2>&1
  4. 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

FilePurpose
client-prod/Alfred.entitlementsCode signing entitlements
client-prod/scripts/build-app.shBuild script with bundle ID
client-prod/AppKitUI/AlfredApp.swiftApp lifecycle, StatusBar
client-prod/Bridge/UserSession.swiftThread-safe session state
client-prod/Bridge/TalkerService.swiftServer communication
client-prod/Bridge/DeviceAuthService.swiftOAuth 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)