| name | ios-debugging |
| description | iOS debugging expert for troubleshooting and profiling. Use when working with Instruments, Time Profiler, Memory Graph, LLDB, breakpoints, console logging, or performance analysis. |
iOS Debugging
Expert guidance for debugging and profiling iOS applications.
Console Logging
os_log (Recommended)
import os.log
let logger = Logger(subsystem: "com.myapp", category: "networking")
logger.debug("Debug message")
logger.info("Info message")
logger.notice("Notice message")
logger.warning("Warning message")
logger.error("Error message")
logger.critical("Critical message")
logger.info("User \(userID, privacy: .private) logged in")
logger.debug("Processing \(items.count) items")
Structured Logging
extension Logger {
static let network = Logger(subsystem: "com.myapp", category: "network")
static let database = Logger(subsystem: "com.myapp", category: "database")
static let ui = Logger(subsystem: "com.myapp", category: "ui")
}
Logger.network.info("Request started: \(url)")
Logger.database.debug("Query executed in \(duration)ms")
Privacy in Logs
logger.info("Email: \(email, privacy: .private)")
logger.info("Token: \(token, privacy: .sensitive)")
logger.info("UserID: \(userID, privacy: .auto)")
logger.info("Item count: \(count, privacy: .public)")
LLDB Debugger
Common Commands
# Print variable
po myVariable
p myVariable
# Print with format
p/x myInt # Hexadecimal
p/t myInt # Binary
p/d myInt # Decimal
# Expression evaluation
expr myVariable = newValue
expr print(myObject.description)
# View memory
memory read myPointer
x/4xw myPointer # 4 words in hex
# Backtrace
bt # Full backtrace
bt 5 # Last 5 frames
# Thread info
thread list
thread select 2
# Continue execution
c # Continue
n # Next (step over)
s # Step (step into)
finish # Step out
Breakpoint Commands
# Set breakpoint
breakpoint set -n functionName
breakpoint set -f ViewController.swift -l 42
# Conditional breakpoint
breakpoint set -n myFunction -c "count > 10"
# Breakpoint action
breakpoint command add 1
> po self.items
> continue
> DONE
# Watchpoint (break on value change)
watchpoint set variable myVariable
Swift-Specific
expr import UIKit
expr let view = unsafeBitCast(0x12345678, to: UIView.self)
expr view.backgroundColor = .red
po _state
Breakpoints in Xcode
Symbolic Breakpoint
Symbol: -[UIViewController viewDidLoad]
Module: UIKitCore
Action: Log Message "VC loaded: @(id)@"
Exception Breakpoint
- Add Exception Breakpoint
- Break on: All Exceptions
- Exception: Objective-C (or All)
Swift Error Breakpoint
- Add Swift Error Breakpoint
- Catches all thrown errors
Conditional Breakpoint
item.id == "problem-id"
5
Instruments
Time Profiler
- Product > Profile (Cmd+I)
- Select Time Profiler
- Record and interact with app
- Analyze call tree
import os.signpost
let log = OSLog(subsystem: "com.myapp", category: .pointsOfInterest)
os_signpost(.begin, log: log, name: "DataProcessing")
processData()
os_signpost(.end, log: log, name: "DataProcessing")
Allocations
- Profile > Allocations
- Look for:
- Memory growth over time
- Transient allocations
- Persistent allocations
Leaks
- Profile > Leaks
- Run app and perform actions
- Check for leaked objects
Network
- Profile > Network
- Monitor:
- Request/response timing
- Data transferred
- Connection issues
Memory Debugging
Memory Graph Debugger
- Run app
- Debug Navigator > Memory
- Click "Debug Memory Graph" button
Finding Retain Cycles
class ViewModel {
var onComplete: (() -> Void)?
func setup(view: View) {
onComplete = { [weak self] in
self?.handleComplete()
}
}
}
Debug Memory Issues
deinit {
print("ViewModel deinitialized")
}
expr import Foundation
expr CFGetRetainCount(myObject)
View Debugging
View Hierarchy Debugger
- Run app
- Click "Debug View Hierarchy" button
- Inspect 3D view hierarchy
Runtime View Modifications
# In LLDB
expr view.backgroundColor = UIColor.red
expr CATransaction.flush()
Print View Hierarchy
po view.value(forKey: "recursiveDescription")
Network Debugging
URLSession Logging
CFNETWORK_DIAGNOSTICS = 3
URLSession.shared.configuration.urlCache?.removeAllCachedResponses()
Charles Proxy / Proxyman
- Install proxy app
- Configure iOS Simulator to use proxy
- Install root certificate
Crash Analysis
Crash Logs
# Location on device
Settings > Privacy > Analytics > Analytics Data
# Symbolicate in Xcode
Window > Devices and Simulators > View Device Logs
Analyzing Crash
Exception Type: EXC_CRASH (SIGABRT)
Thread 0 Crashed:
0 libsystem_kernel.dylib __pthread_kill
1 libsystem_c.dylib abort
2 MyApp ViewController.buttonTapped + 123
Performance Debugging
Measure Code Performance
import os.signpost
let signpostID = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "Operation", signpostID: signpostID)
performOperation()
os_signpost(.end, log: log, name: "Operation", signpostID: signpostID)
let start = CFAbsoluteTimeGetCurrent()
performOperation()
let duration = CFAbsoluteTimeGetCurrent() - start
print("Duration: \(duration * 1000)ms")
Main Thread Checker
Edit Scheme > Run > Diagnostics
✓ Main Thread Checker
Thread Sanitizer
Edit Scheme > Run > Diagnostics
✓ Thread Sanitizer
Address Sanitizer
Edit Scheme > Run > Diagnostics
✓ Address Sanitizer
Debug-Only Code
#if DEBUG
print("Debug only message")
#endif
assert(condition, "Message")
precondition(condition, "Message")
fatalError("Unimplemented")
Common Issues
UI Not Updating
DispatchQueue.main.async {
self.label.text = newValue
}
await MainActor.run {
self.updateUI()
}
Optional Unwrapping Crash
if let value = optionalValue {
use(value)
}
guard let value = optionalValue else {
return
}
Debugging SwiftUI
let _ = print("Body evaluated")
let _ = Self._printChanges()
Apple Documentation