Quick reference for AI agents working with Xcode projects
- Always capture logs with the wrapper scripts - They include
--level debugto capture useful diagnostic information - Use the build wrapper - It captures output to searchable files
- Search logs with grep - Don't load entire log files into context
- Wait for app to run - Logs only capture when the app is actually running
- Never use
xcodebuild -clean- It's unnecessary and slows builds - Never capture logs without starting the app - You'll get empty files
- Never load entire build/log files - Use grep to find what you need
- Never assume the simulator - Verify it's running with
xcrun simctl list
This is the VERIFIED working workflow. Follow these steps exactly.
CRITICAL: Use the ./.claude/scripts/xcodebuild wrapper to capture build output to searchable files. Always include -derivedDataPath to make the .app file location predictable.
cd /path/to/your/project
# For Simulator (iPad/iPhone)
./.claude/scripts/xcodebuild \
-project orchestrator.xcodeproj \
-scheme orchestrator \
-destination 'platform=iOS Simulator,name=iPad Pro 11-inch (M5)' \
-derivedDataPath ./build/DerivedData
# For Simulator (visionOS)
./.claude/scripts/xcodebuild \
-project YourApp.xcodeproj \
-scheme YourApp \
-destination 'platform=visionOS Simulator,name=Apple Vision Pro' \
-derivedDataPath ./build/DerivedData
# For Device (iPad/iPhone)
./.claude/scripts/xcodebuild \
-project orchestrator.xcodeproj \
-scheme orchestrator \
-destination 'generic/platform=iOS' \
-derivedDataPath ./build/DerivedData
# For Device (visionOS)
./.claude/scripts/xcodebuild \
-project YourApp.xcodeproj \
-scheme YourApp \
-destination 'generic/platform=visionOS' \
-derivedDataPath ./build/DerivedDataThe wrapper captures output to: ./build/xcodebuild/build-TIMESTAMP.txt
Expected output: ** BUILD SUCCEEDED **
Built app location (Simulator):
- iOS:
./build/DerivedData/Build/Products/Debug-iphonesimulator/YourApp.app - visionOS:
./build/DerivedData/Build/Products/Debug-xrsimulator/YourApp.app
Built app location (Device):
- iOS:
./build/DerivedData/Build/Products/Debug-iphoneos/YourApp.app - visionOS:
./build/DerivedData/Build/Products/Debug-xros/YourApp.app
IMPORTANT: The .app name uses the product name (often capitalized), not the scheme name:
- Scheme:
orchestrator→ Product:Orchestrator.app
If build fails, search the output:
# Find the most recent build output
LAST_BUILD=$(ls -t ./build/xcodebuild/build-*.txt | head -n 1)
# Search for errors
grep -i "error:" "$LAST_BUILD"
# Search with context (3 lines before/after)
grep -C 3 -i "error:" "$LAST_BUILD"# Install on booted simulator
xcrun simctl install booted \
"./build/DerivedData/Build/Products/Debug-iphonesimulator/Orchestrator.app"
# For visionOS
xcrun simctl install booted \
"./build/DerivedData/Build/Products/Debug-xrsimulator/YourApp.app"Expected: Silent success (no output)
Verify installation:
xcrun simctl list apps booted | grep -i orchestratorFor Simulator:
./.claude/scripts/capture-logs com.groovejones.orchestratorFor Device:
# Device logging uses a different command (see device section below)
xcrun devicectl device info logs stream \
--device <device-id> \
--style compact \
--predicate 'subsystem == "com.groovejones.orchestrator"' \
> ./build/logs/logs-device-$(date +%Y%m%d-%H%M%S).txt &
# Save PID for cleanup
echo $! > ./build/logs/.last-device-capture-pidFor Simulator:
xcrun simctl launch booted com.groovejones.orchestratorExpected: com.groovejones.orchestrator: 56532 (PID number)
For Device:
# Launch via Xcode (Cmd+R) or:
xcrun devicectl device process launch \
--device <device-id> \
com.groovejones.orchestratorDO NOT skip this step! The app needs time to initialize and emit logs.
sleep 10Interact with the app if needed to trigger the behavior you're debugging.
For Simulator:
./.claude/scripts/stop-logsFor Device:
# Kill the background log process
kill $(cat ./build/logs/.last-device-capture-pid)
rm ./build/logs/.last-device-capture-pid# Check file size (should be > 500 bytes if logs captured)
ls -lh ./build/logs/logs-*.txt
# View recent logs
tail -50 ./build/logs/logs-*.txt
# Search for errors
grep -i error ./build/logs/logs-*.txt
# Search with context
grep -C 3 "stream_state_changed" ./build/logs/logs-*.txtExpected content example:
2025-11-21 18:21:09.946 I Orchestrator[56532:1ceff69] [com.groovejones.orchestrator:streaming] [Orchestrator] [Streaming] stream_receiver_listening | category=streaming component=AppUIModel port=22345
VERIFIED: Device logging uses the regular log stream command (not simctl or devicectl). When a device is connected via USB, log stream automatically captures from it.
- Device connected via USB
- Device in Developer Mode
- Device trusted on this Mac
xcrun devicectl list devicesLook for your device with status "connected" or "available (paired)".
Method 1: Manual (works now)
cd /path/to/your/project
# Start log capture from connected device
xcrun log stream \
--level debug \
--style compact \
--predicate 'subsystem == "com.groovejones.orchestrator"' \
> ./build/logs/logs-device-$(date +%Y%m%d-%H%M%S).txt 2>&1 &
# Save the PID manually
DEVICE_LOG_PID=$!
echo $DEVICE_LOG_PID > ./build/logs/.last-device-capture-pid
# Launch your app via Xcode (Cmd+R targeting the device)
# Wait for logs to accumulate
sleep 10
# Stop capture
kill $(cat ./build/logs/.last-device-capture-pid)
rm ./build/logs/.last-device-capture-pid
# View logs
tail -50 ./build/logs/logs-device-*.txtMethod 2: Using wrapper script (if available)
The capture-logs script is designed for simulators. For devices, use the manual method above until we create a device-specific wrapper.
| Aspect | Simulator | Physical Device |
|---|---|---|
| Command | xcrun simctl spawn booted log stream |
xcrun log stream |
| Device selection | --simulator <id> or booted |
Automatic (connected device) |
| Multiple devices | Specify simulator ID | May capture from multiple connected devices |
| Requires | Simulator booted | Device connected via USB |
- Single device: If you have multiple devices connected, disconnect extras to avoid mixed logs
- Process filter: Add
--process Orchestratorto filter by process name - Wi-Fi devices: Device must be on same network and paired for wireless debugging
- Check logs are from device: Look for device name in log output headers
The Problem: The script was missing --level debug flag, so it only captured Info and Error level logs. Most useful debug statements (Logger.debug(), print(), detailed diagnostics) are at Debug level.
The Fix: Now includes:
xcrun simctl spawn booted log stream --level debug --style compact --predicate "subsystem == 'com.example.app'"Cause: App not running, or wrong subsystem name Solution:
- Verify app is running in simulator
- Check subsystem name matches your app's logging setup
- Try broader predicate:
com.exampleinstead ofcom.example.app.specific
Cause: Wrong project path or scheme name Solution:
- Run
cd ../orchestrator && xcodebuild -listto see available schemes - Use exact scheme name from the output
Cause: Simulator not booted Solution:
- Check:
xcrun simctl list | grep Booted - Boot simulator in Xcode or use specific simulator ID
./build/
├── xcodebuild/
│ └── build-20251121-143022.txt # Build output with timestamp
└── logs/
└── logs-20251121-143500.txt # App logs with timestamp
# Build errors
grep -i "error:" ./build/xcodebuild/build-*.txt
# Build warnings
grep -i "warning:" ./build/xcodebuild/build-*.txt
# Test failures
grep -i "failed" ./build/xcodebuild/build-*.txt
# Runtime crashes
grep -i "crash\|exception\|fatal" ./build/logs/logs-*.txt
# Specific class/function
grep -C 5 "MyClass" ./build/logs/logs-*.txt
# Count occurrences
grep -c "ERROR" ./build/logs/logs-*.txtInstead of loading entire files:
# ❌ DON'T: Load entire 50MB build log
cat ./build/xcodebuild/build-latest.txt
# ✅ DO: Search for what you need
grep -i error ./build/xcodebuild/build-*.txtBefore claiming "logs are useless" or "build failed mysteriously":
- Used the wrapper scripts (not raw xcodebuild/simctl)
- Started log capture BEFORE launching app
- Verified app actually ran (not just built)
- Used grep to search logs (not cat to dump everything)
- Checked the correct timestamp file (most recent)
- Searched for relevant keywords (class names, error types)
See building-with-xcode.md for complete documentation including:
- Advanced grep patterns
- Platform-specific builds
- Performance profiling
- Integration with Claude Code
Last Updated: 2025-11-21 (Added --level debug fix)