7.7 KiB
CLI Observability
Complete debugging and monitoring without opening Xcode. Claude has full visibility into build errors, runtime logs, crashes, memory issues, and network traffic.
```bash # Install observability tools (one-time) brew tap ldomaradzki/xcsift && brew install xcsift brew install mitmproxy xcbeautify ```<build_output>
Build Error Parsing
xcsift converts verbose xcodebuild output to token-efficient JSON for AI agents:
xcodebuild -project MyApp.xcodeproj -scheme MyApp 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:
import os
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 Running App
# All logs from your app
log stream --level debug --predicate 'subsystem == "com.yourcompany.MyApp"'
# Filter by category
log stream --level debug \
--predicate 'subsystem == "com.yourcompany.MyApp" AND category == "Network"'
# Errors only
log stream --predicate 'subsystem == "com.yourcompany.MyApp" AND messageType == error'
# JSON output for parsing
log stream --level debug --style json \
--predicate 'subsystem == "com.yourcompany.MyApp"'
Search Historical Logs
# Last hour
log show --predicate 'subsystem == "com.yourcompany.MyApp"' --last 1h
# Export to file
log show --predicate 'subsystem == "com.yourcompany.MyApp"' --last 1h > logs.txt
</runtime_logging>
<crash_analysis>
Crash Logs
Find Crashes
# List crash reports
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>
## LLDB DebuggingAttach to Running App
# By name
lldb -n MyApp
# By PID
lldb -p $(pgrep MyApp)
Launch and Debug
lldb ./build/Build/Products/Debug/MyApp.app/Contents/MacOS/MyApp
(lldb) run
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)
<memory_debugging>
Memory Debugging
Leak Detection
# Check running process for leaks
leaks MyApp
# Run with leak check at exit
leaks --atExit -- ./MyApp
# With stack traces (shows where leak originated)
MallocStackLogging=1 ./MyApp &
leaks MyApp
Heap Analysis
# Show heap summary
heap MyApp
# Show allocations of specific class
heap MyApp -class NSString
# Virtual memory regions
vmmap --summary MyApp
Profiling with xctrace
# List templates
xcrun xctrace list templates
# Time Profiler
xcrun xctrace record \
--template 'Time Profiler' \
--time-limit 30s \
--output profile.trace \
--launch -- ./MyApp.app/Contents/MacOS/MyApp
# Leaks
xcrun xctrace record \
--template 'Leaks' \
--time-limit 5m \
--attach $(pgrep MyApp) \
--output leaks.trace
# Export data
xcrun xctrace export --input profile.trace --toc
</memory_debugging>
## SanitizersEnable via xcodebuild flags:
# Address Sanitizer (memory errors, buffer overflows)
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-enableAddressSanitizer YES
# Thread Sanitizer (race conditions)
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-enableThreadSanitizer YES
# Undefined Behavior Sanitizer
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-enableUndefinedBehaviorSanitizer YES
Note: ASAN and TSAN cannot run simultaneously.
<network_inspection>
Network Traffic Inspection
mitmproxy Setup
# Run proxy (defaults to localhost:8080)
mitmproxy # TUI
mitmdump # CLI output only
Configure macOS Proxy
# 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
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 \
-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
</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>
<standard_debug_workflow>
Standard Debug Workflow
# 1. Build with error parsing
xcodebuild -project MyApp.xcodeproj -scheme MyApp build 2>&1 | xcsift
# 2. Run with log streaming (background terminal)
log stream --level debug --predicate 'subsystem == "com.yourcompany.MyApp"' &
# 3. Launch app
open ./build/Build/Products/Debug/MyApp.app
# 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 | ✓ | log stream |
| Crash symbolication | ✓ | atos, lldb |
| Breakpoints/debugging | ✓ | lldb |
| Memory leaks | ✓ | leaks, xctrace |
| CPU profiling | ✓ | xctrace |
| Network inspection | ✓ | mitmproxy |
| Test results | ✓ | xcresulttool |
| Sanitizers | ✓ | xcodebuild flags |
| View hierarchy | ⚠️ | _printChanges() only |
| GPU debugging | ✗ | Requires Xcode |
| </cli_vs_xcode> |