CLI Observability
Complete debugging and monitoring without opening Xcode. Claude has full visibility into build errors, runtime logs, crashes, memory issues, and network traffic.
Overview
CLI Observability
Complete debugging and monitoring without opening Xcode. Claude has full visibility into build errors, runtime logs, crashes, memory issues, and network traffic.
<prerequisites> ```bash # Install observability tools (one-time) brew tap ldomaradzki/xcsift && brew install xcsift brew install mitmproxy xcbeautify ``` </prerequisites><build_output>
Build Error Parsing
xcsift converts verbose xcodebuild output to token-efficient JSON for AI agents:
xcodebuild -project MyApp.xcodeproj -scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
build 2>&1 | xcsift
Output includes structured errors with file paths and line numbers:
{
"status": "failed",
"errors": [
{"file": "/path/File.swift", "line": 42, "message": "Type mismatch..."}
]
}
Alternative (human-readable):
xcodebuild build 2>&1 | xcbeautify
</build_output>
<runtime_logging>
Runtime Logs
In-App Logging Pattern
Add to all apps:
extension Logger {
static let app = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "App")
static let network = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "Network")
static let data = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "Data")
}
// Usage
Logger.network.debug("Request: \\(url)")
Logger.data.error("Save failed: \\(error)")
Stream Logs from Simulator
# All logs from your app
xcrun simctl spawn booted log stream --level debug \\
--predicate 'subsystem == "com.yourcompany.MyApp"'
# Filter by category
xcrun simctl spawn booted log stream --level debug \\
--predicate 'subsystem == "com.yourcompany.MyApp" AND category == "Network"'
# Errors only
xcrun simctl spawn booted log stream \\
--predicate 'subsystem == "com.yourcompany.MyApp" AND messageType == error'
# JSON output for parsing
xcrun simctl spawn booted log stream --level debug --style json \\
--predicate 'subsystem == "com.yourcompany.MyApp"'
Search Historical Logs
# Collect logs from simulator
xcrun simctl spawn booted log collect --output sim_logs.logarchive
# Search collected logs
log show sim_logs.logarchive --predicate 'subsystem == "com.yourcompany.MyApp"'
</runtime_logging>
<crash_analysis>
Crash Logs
Find Crashes (Simulator)
# Simulator crash logs
ls ~/Library/Logs/DiagnosticReports/ | grep MyApp
# View latest crash
cat ~/Library/Logs/DiagnosticReports/MyApp_*.ips | head -200
Symbolicate with atos
# Get load address from "Binary Images:" section of crash report
xcrun atos -arch arm64 \\
-o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \\
-l 0x104600000 \\
0x104605ca4
# Verify dSYM matches
xcrun dwarfdump --uuid MyApp.app.dSYM
Symbolicate with LLDB
xcrun lldb
(lldb) command script import lldb.macosx.crashlog
(lldb) crashlog /path/to/crash.ips
</crash_analysis>
<debugger> ## LLDB DebuggingLaunch with Console Output
# Launch and see stdout/stderr
xcrun simctl launch --console booted com.yourcompany.MyApp
Attach to Running App
# By name
lldb -n MyApp
# By PID
lldb -p $(pgrep MyApp)
# Wait for app to launch
lldb -n MyApp --wait-for
Essential Commands
# Breakpoints
(lldb) breakpoint set --file ContentView.swift --line 42
(lldb) breakpoint set --name "AppState.addItem"
(lldb) breakpoint set --name saveItem --condition 'item.name == "Test"'
# Watchpoints (break when value changes)
(lldb) watchpoint set variable self.items.count
# Execution
(lldb) continue # or 'c'
(lldb) next # step over
(lldb) step # step into
(lldb) finish # step out
# Inspection
(lldb) p variable
(lldb) po object
(lldb) frame variable # all local vars
(lldb) bt # backtrace
(lldb) bt all # all threads
# Evaluate expressions
(lldb) expr self.items.count
(lldb) expr self.items.append(newItem)
</debugger>
<memory_debugging>
Memory Debugging
Leak Detection (Simulator)
# Check running process for leaks
leaks MyApp
Profiling with xctrace
# List templates
xcrun xctrace list templates
# Time Profiler
xcrun xctrace record \\
--template 'Time Profiler' \\
--time-limit 30s \\
--output profile.trace \\
--device booted \\
--launch -- com.yourcompany.MyApp
# Leaks
xcrun xctrace record \\
--template 'Leaks' \\
--time-limit 5m \\
--device booted \\
--attach MyApp \\
--output leaks.trace
# Export data
xcrun xctrace export --input profile.trace --toc
</memory_debugging>
<sanitizers> ## SanitizersEnable via xcodebuild flags:
# Address Sanitizer (memory errors, buffer overflows)
xcodebuild test \\
-project MyApp.xcodeproj \\
-scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
-enableAddressSanitizer YES
# Thread Sanitizer (race conditions)
xcodebuild test \\
-project MyApp.xcodeproj \\
-scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
-enableThreadSanitizer YES
# Undefined Behavior Sanitizer
xcodebuild test \\
-project MyApp.xcodeproj \\
-scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
-enableUndefinedBehaviorSanitizer YES
Note: ASAN and TSAN cannot run simultaneously. </sanitizers>
<network_inspection>
Network Traffic Inspection
mitmproxy Setup
# Run proxy (defaults to localhost:8080)
mitmproxy # TUI
mitmdump # CLI output only
Configure macOS Proxy (Simulator uses host network)
# Enable
networksetup -setwebproxy "Wi-Fi" 127.0.0.1 8080
networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 8080
# Disable when done
networksetup -setwebproxystate "Wi-Fi" off
networksetup -setsecurewebproxystate "Wi-Fi" off
Install Certificate on Simulator
xcrun simctl keychain booted add-root-cert ~/.mitmproxy/mitmproxy-ca-cert.pem
Important: Restart simulator after proxy/cert changes.
Log Traffic
# Log all requests
mitmdump -w traffic.log
# Filter by domain
mitmdump --filter "~d api.example.com"
# Verbose (show bodies)
mitmdump -v
</network_inspection>
<test_results>
Test Result Parsing
# Run tests with result bundle
xcodebuild test \\
-project MyApp.xcodeproj \\
-scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
-resultBundlePath TestResults.xcresult
# Get summary
xcrun xcresulttool get test-results summary --path TestResults.xcresult
# Export as JSON
xcrun xcresulttool get --path TestResults.xcresult --format json > results.json
# Coverage report
xcrun xccov view --report TestResults.xcresult
# Coverage as JSON
xcrun xccov view --report --json TestResults.xcresult > coverage.json
Accessibility Audits (Xcode 15+)
Add to UI tests:
func testAccessibility() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit()
}
Run via CLI:
xcodebuild test \\
-project MyApp.xcodeproj \\
-scheme MyAppUITests \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
-only-testing:MyAppUITests/AccessibilityTests
</test_results>
<swiftui_debugging>
SwiftUI Debugging
Track View Re-evaluation
var body: some View {
let _ = Self._printChanges() // Logs what caused re-render
VStack {
// ...
}
}
Dump Objects
let _ = dump(someObject) // Full object hierarchy to console
Note: No CLI equivalent for Xcode's visual view hierarchy inspector. Use logging extensively. </swiftui_debugging>
<simulator_management>
Simulator Management
# List simulators
xcrun simctl list devices
# Boot simulator
xcrun simctl boot "iPhone 16"
open -a Simulator
# Install app
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/MyApp.app
# Launch app
xcrun simctl launch booted com.yourcompany.MyApp
# Launch with console output
xcrun simctl launch --console booted com.yourcompany.MyApp
# Screenshot
xcrun simctl io booted screenshot ~/Desktop/screenshot.png
# Video recording
xcrun simctl io booted recordVideo ~/Desktop/recording.mov
# Set location
xcrun simctl location booted set 37.7749,-122.4194
# Send push notification
xcrun simctl push booted com.yourcompany.MyApp notification.apns
# Reset simulator
xcrun simctl erase booted
</simulator_management>
<device_debugging>
Device Debugging (iOS 17+)
# List devices
xcrun devicectl list devices
# Install app
xcrun devicectl device install app --device <udid> MyApp.app
# Launch app
xcrun devicectl device process launch --device <udid> com.yourcompany.MyApp
</device_debugging>
<standard_debug_workflow>
Standard Debug Workflow
# 1. Build with error parsing
xcodebuild -project MyApp.xcodeproj -scheme MyApp \\
-destination 'platform=iOS Simulator,name=iPhone 16' \\
build 2>&1 | xcsift
# 2. Boot simulator and start log streaming (background terminal)
xcrun simctl boot "iPhone 16"
open -a Simulator
xcrun simctl spawn booted log stream --level debug \\
--predicate 'subsystem == "com.yourcompany.MyApp"' &
# 3. Install and launch
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/MyApp.app
xcrun simctl launch booted com.yourcompany.MyApp
# 4. If crash occurs
cat ~/Library/Logs/DiagnosticReports/MyApp_*.ips | head -100
# 5. Memory check
leaks MyApp
# 6. Deep debugging
lldb -n MyApp
</standard_debug_workflow>
<cli_vs_xcode>
What CLI Can and Cannot Do
| Task | CLI | Tool |
|---|---|---|
| Build errors | ✓ | xcsift |
| Runtime logs | ✓ | simctl log stream |
| Crash symbolication | ✓ | atos, lldb |
| Breakpoints/debugging | ✓ | lldb |
| Memory leaks | ✓ | leaks, xctrace |
| CPU profiling | ✓ | xctrace |
| Network inspection | ✓ | mitmproxy |
| Test results | ✓ | xcresulttool |
| Accessibility audit | ✓ | UI tests |
| Sanitizers | ✓ | xcodebuild flags |
| View hierarchy | ⚠️ | _printChanges() only |
| GPU debugging | ✗ | Requires Xcode |
| </cli_vs_xcode> |