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. This is lesson 3 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, and your progress syncs across the web and the CoddyKit app. The Swift Academy course includes 4 lessons in total.
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.
Learn Swift with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 122
- Lessons
- 409
Frequently Asked Questions
Is the “Main Thread Checker and Hangs” lesson free?
Yes — any 3 lessons of the Swift Academy learning path, including “Main Thread Checker and Hangs”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Swift Academy course includes 4 lessons in total.
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, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.
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.
All lessons in this course
- Instruments Basics: Time Profiler
- Allocations and Leaks Instruments
- Main Thread Checker and Hangs
- Optimising Hot Paths: COW, Inlining and Specialisation