Main Thread Checker and Hangs
Detecting UI work on background threads and resolving main-thread hangs.
Main Thread Checker and Hangs is a free Swift Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Main Thread Checker
The Main Thread Checker (MTC) is a Xcode diagnostic that reports UIKit/AppKit API calls made from background threads at runtime.
// Enabled by default in Xcode Debug scheme:
// Product → Scheme → Edit → Diagnostics → ☑ Main Thread Checker
// Violating calls log a purple runtime warning in XcodeCommon MTC Violations
Updating UI from completion handlers or background queues are the most frequent violations.
URLSession.shared.dataTask(with: url) { data, _, _ in
self.label.text = "Done" // VIOLATION: background thread
}.resume()
// Fix:
DispatchQueue.main.async { self.label.text = "Done" }MTC with async/await
Marking a class or function with @MainActor guarantees all its methods run on the main thread.
@MainActor
final class ProfileViewModel: ObservableObject {
@Published var name = ""
func load() async {
name = try? await fetchName() // always on main thread
}
}What Are Hangs?
A hang is when the main thread is blocked for too long, making the app unresponsive. iOS watchdog terminates apps that hang at launch (>20s) or after a user action.
// Common causes:
// - Synchronous network calls on main thread
// - Heavy computation blocking main run loop
// - Deadlocks between DispatchQueuesInstruments Hangs Template
The Hangs instrument in Xcode 14+ records hang events with full stack traces to diagnose main thread blockage.
// Instruments → Hangs template
// Shows hang duration (ms), call stack, and responsible frame
// Look for synchronous I/O or heavy loopsMain Thread Checker in Tests
MTC also fires during UI tests. Configure it in the test scheme to catch violations in automated UI flows.
// XCTestScheme Diagnostics → ☑ Main Thread Checker
// UI test violation = instant fail in CIMetricKit Hang Reports
MetricKit collects hang reports from production apps in MXHangDiagnosticPayload.
func didReceive(_ payloads: [MXDiagnosticPayload]) {
payloads.compactMap { $0.hangDiagnostics }.flatMap { $0 }.forEach {
print($0.callStackTree)
}
}Detecting Hangs with Instruments
Record a trace and look for time intervals where the Main Thread is executing but the display link isn't firing (dropped frames).
// Time Profiler: look for long CPU bursts on main thread
// Core Animation: FPS drops to 0 during hang
// Hangs instrument: highlights exact frames where hang occurredAsync Fixes for Common Hangs
Move CPU-heavy work off the main thread using Task.detached or async let.
// HANG:
let processed = heavyProcessing(image) // blocks main thread
// FIX:
let processed = await Task.detached(priority: .userInitiated) {
heavyProcessing(image)
}.valueAvoiding Synchronous APIs on Main Thread
Replace synchronous file I/O and network calls with async equivalents or move them to a background queue.
// HANG: synchronous file read on main thread
let data = try! Data(contentsOf: largeFileURL)
// FIX:
let data = try await Task.detached { try Data(contentsOf: largeFileURL) }.valueMain Run Loop and Timers
Long-running operations on the main run loop (in RunLoop.main callbacks) also cause hangs. Move computation off the main loop.
// AVOID: heavy work in main-thread timer callback
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
processAllItems() // slow!
}
// PREFER: dispatch to background
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
Task.detached { processAllItems() }
}Quick Check
What does adding @MainActor to a class guarantee about its method calls?
Lesson Recap
Enable Main Thread Checker in Debug scheme to catch UI-from-background violations. Use @MainActor on ViewModels and views. Move heavy work off the main thread with Task.detached or background queues. Use the Hangs instrument and MetricKit MXHangDiagnosticPayload to diagnose real-world hangs.
Frequently asked questions
Is the “Main Thread Checker and Hangs” lesson free?
Yes — the full text of “Main Thread Checker and Hangs” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Main Thread Checker and Hangs”?
Detecting UI work on background threads and resolving main-thread hangs. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Main Thread Checker and Hangs” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.