SignalLab is a hands-on iOS learning app for junior and intermediate developers who want practical experience debugging real application problems with Xcode and Instruments.
The project is a working guided lab environment rather than a generic sample app. Each lab contains a realistic bug, diagnostic setup, or performance problem, along with a reproducible trigger and a recommended investigation workflow.
For CLI build defaults, preferred simulator settings, and other machine-readable notes aimed at contributors and coding agents, see AGENTS.md in this repository.
SignalLab exists to help developers build confidence with debugging by practicing on controlled, repeatable scenarios that mirror common production issues.
Instead of reading about breakpoints, leaks, hangs, and profiling in the abstract, learners will launch a scenario, trigger the issue, inspect the evidence with Apple tooling, identify the cause, and validate the fix.
The app should feel like a polished, modern Apple-platform developer tool while remaining approachable for engineers who are still building intuition around stacks, memory ownership, responsiveness, and performance.
SignalLab is primarily intended for:
- Junior iOS developers learning how to debug with Xcode
- Intermediate iOS developers who want more practical experience with Instruments
- Mentors, instructors, and interview coaches who want concrete debugging exercises
- Engineers who learn best by investigating realistic bugs rather than reading isolated examples
SignalLab should help learners:
- Understand how to use breakpoints and inspect stack traces
- Build intuition around call stacks, frames, locals, and caller relationships
- Diagnose non-crashing logic bugs with line, conditional, and action breakpoints
- Find and explain memory leaks and ownership problems
- Identify hangs and main-thread abuse
- Use Time Profiler to locate hot code paths and repeated expensive work
- Compare broken and fixed implementations using the same debugging workflow
- Build a repeatable mental model for investigating app problems instead of guessing
The first version of SignalLab is intentionally constrained.
It is not intended to be:
- A general-purpose reference for every Xcode feature
- A broad survey of every possible Instruments template
- A production observability platform
- A replacement for Apple documentation
- A highly advanced concurrency lab on day one
- A networking-dependent demo that relies on unstable external services
The early focus is clarity, reproducibility, and strong teaching value.
SignalLab is structured as a catalog of self-contained debugging labs.
Each lab should provide:
- A focused scenario with one dominant lesson
- A clear symptom that is visible in the UI or behavior
- A reproducible trigger that works quickly and consistently
- A recommended first tool to investigate the issue
- Hints that guide the learner without immediately giving away the answer
- A fixed mode or fixed implementation for comparison
- Notes explaining why the issue occurred and why the fix works
This approach keeps the learning experience concrete and repeatable.
SignalLab uses a modern, dark-forward visual style inspired by Apple developer tools and observability interfaces.
The intended theme is:
- Calm, precise, and technical
- Beautiful and modern without becoming flashy
- Glassy, layered, and depth-aware where appropriate
- Highly readable with strong visual hierarchy
- Semantic in its use of color for warnings, faults, memory, success, and neutral tooling states
This theme supports the product goal of making debugging feel intentional and approachable.
SignalLab is meant to support both guided learning and free exploration.
Each lab should be usable in several ways:
- Independent exploration by launching the lab and investigating the symptom
- Guided instruction with hints and recommended tools
- Mentor-led walkthroughs in training or interview prep settings
- Before-and-after comparison using broken and fixed implementations
The app should emphasize reasoning, not memorization.
The app currently ships a locked MVP sequence of six core labs, followed by scheme-diagnostics entries and additional Phase 2 scenarios. The summaries below match that teaching order; SignalLab/SignalLab/Shared/LabDomain/LabCatalog.swift is the source of truth for titles, slugs, and copy.
MVP labs:
- Crash Lab
- Exception Breakpoint Lab
- Breakpoint Lab
- Memory Graph Lab
- Hang Lab
- CPU Hotspot Lab
Additional working catalog labs:
- Thread Performance Checker Lab
- Zombie Objects Lab
- Thread Sanitizer Lab
- Malloc Stack Logging Lab
- Retain Cycle Lab
- Heap Growth Lab
- Deadlock Lab
- Background Thread UI Lab
- Main Thread I/O Lab
- Scroll Hitch Lab
- Startup Signpost Lab
- Concurrency Isolation Lab
This lab teaches the default Xcode stop after a crash: finding the first relevant app frame, inspecting locals, and moving one caller up for context—before treating exception breakpoints as the first tool.
Scenario: A sample import flow loads malformed local data and crashes because the parser makes unsafe assumptions.
Key learning goals:
- Find the first relevant frame in your code after a crash
- Inspect locals and caller context in the stopped debugger
- Identify the bad assumption in the parser and how Fixed mode validates input safely
This lab compares debugger stop policy: the same failure family as Crash Lab, once with Xcode’s default stop and again with an Exception Breakpoint, so you can articulate what the extra breakpoint adds.
Key learning goals:
- Compare default crash stop vs Exception Breakpoint on the same repro
- Recognize when changing stop policy gives clearer or earlier context
- Keep this lab distinct from Breakpoint Lab (logic bugs while the app keeps running)
This lab teaches practical use of one line breakpoint for a logic bug that does not crash.
Scenario: A student order shows the wrong final total because the discount calculation applies 5% when the expected discount is 20%.
Key learning goals:
- Use a line breakpoint when the app keeps running but produces a wrong result
- Inspect local variables at the paused calculation line before changing code
- Explain the bad total from one visible calculation input:
discountPercent
This lab teaches the first Memory Graph workflow: navigate the left Memory Graph hierarchy to one named app object and read the arrow to the object it keeps alive.
Scenario:
An open note should be easy to inspect in Memory Graph. MemoryGraphOpenNoteHolder keeps MemoryGraphOpenNote alive so learners can practice following one straight ownership path before diagnosing retain cycles.
Key learning goals:
- Use the Memory Graph left navigator instead of relying on the default canvas selection
- Navigate
SignalLab.debug.dylib->MemoryGraphOpenNoteHolderbefore reading the graph - Read the arrow from
MemoryGraphOpenNoteHoldertoMemoryGraphOpenNoteas "the holder keeps this note alive" - Use the right inspector Backtrace to jump from the live object to the source line that allocated it
- Learn the basic "who owns this object?" workflow before the later Retain Cycle Lab
This lab teaches hang investigation and main-thread responsiveness analysis.
Scenario: A report-loading flow performs heavy JSON parsing and transformation work on the main thread, causing the UI to freeze.
Key learning goals:
- Recognize a visible hang
- Pause execution during a freeze and inspect threads
- Identify work that should not be happening on the main thread
- Compare broken and fixed responsiveness behavior
This lab teaches Time Profiler and hot-path investigation.
Scenario: A searchable list becomes sluggish because each keystroke triggers repeated expensive work, unnecessary sorting, and repeated helper creation.
Key learning goals:
- Profile a slow interaction with Time Profiler
- Identify the hottest functions in the trace
- Separate core causes from framework noise
- Validate that the optimized implementation improves responsiveness
Several topics below already have catalog entries (see Tasks.md MVP exit criteria); this list remains for additional depth or new templates as Apple tooling evolves.
- Deeper heap and allocation workflows beyond the current Heap Growth and Malloc Stack Logging labs
- Broader race and isolation coverage beyond Thread Sanitizer and Concurrency Isolation labs
- Additional Instruments templates or platform-specific rendering tools as Apple ships them
- Glossary, structured hint progression, and mentor-style prompts (see roadmap phases)
Establish the reusable structure for the app.
Planned work:
- Create the app shell and lab catalog
- Define shared lab metadata and navigation patterns
- Build reusable UI for lab descriptions, triggers, hints, and fixed-mode comparison
- Set up documentation structure for labs and investigation guides
Ship the six MVP labs in locked curriculum order (see LabRefinement.md).
Delivered in the app:
- Crash Lab
- Exception Breakpoint Lab
- Breakpoint Lab
- Memory Graph Lab
- Hang Lab
- CPU Hotspot Lab
Goal: Ship a strong MVP that already teaches the most important debugging workflows.
Broaden the curriculum into scheme diagnostics and more advanced debugging scenarios.
Post-MVP scheme diagnostics (in catalog, order per LabRefinement.md):
- Thread Performance Checker Lab (
thread_performance_checker) — guided, ties to Hang Lab + Xcode scheme diagnostics - Zombie Objects Lab (
zombie_objects) - Thread Sanitizer Lab (
thread_sanitizer) - Malloc Stack Logging Lab (
malloc_stack_logging) - Retain Cycle Lab (
retain_cycle) — preserved slug and terminology, now later than the first Memory Graph lesson
Shipped (Phase 2 curriculum slice):
- Heap Growth Lab (
heap_growth) — unbounded buffer retention vs capped ring buffer (contrast with retain cycles) - Deadlock Lab (
deadlock) — main-queuesyncself-deadlock vs safe main-actor work - Background Thread UI Lab (
background_thread_ui) — notification from detached task vsMainActordelivery before UI observers run - Main Thread I/O Lab (
main_thread_io) — synchronous repeated file reads on main vs detachedData(contentsOf:) - Scroll Hitch Lab (
scroll_hitch) — per-row compositing + shadow cost during scroll vs lighter chrome - Startup Signpost Lab (
startup_signpost) — phased main-thread work with vs withoutos_signpost/ Points of Interest - Concurrency Isolation Lab (
concurrency_isolation) — unstructuredTask.detachedordering vs sequentialasyncwork (before reaching for Thread Sanitizer)
Still on the roadmap:
- Additional Instruments templates or platform-specific rendering tools as Apple ships them
- Deeper Swift 6 strict-concurrency curriculum beyond Concurrency Isolation Lab
Turn the app into a more complete teaching product.
Planned work:
- Progressive difficulty levels
- Structured hint system
- Glossary of debugging terms
- Mentor and workshop prompts
- Lab completion and validation checklists
Improve reproducibility and maintainability.
Planned work:
- Repeat-trigger controls for scenarios
- Optional automation for selected labs
- Before-and-after verification checklists
- Consistent comparison workflows for broken and fixed traces
To keep SignalLab useful and teachable, every lab should follow a consistent set of principles.
Each lab should teach one dominant concept. Supporting details are fine, but the learner should always know what the main lesson is.
A learner should be able to trigger the issue quickly. If the issue takes too long to reproduce, the lab becomes frustrating and loses teaching value.
The issue should be visible and understandable.
Examples:
- The app crashes
- The UI freezes
- Results are wrong
- An object does not deallocate
- Search becomes sluggish
Whenever possible, each lab should provide a broken and fixed mode so the learner can compare behavior and validate the results of the investigation.
The project should prefer realistic app patterns over contrived algorithm puzzles.
Examples of good scenario sources:
- Data import and parsing
- Search and filtering
- Timers and closures
- Main-thread JSON processing
- Expensive repeated UI-related work
The product is built around learning Apple’s own debugging stack, especially:
- Xcode breakpoints and LLDB
- Call stack and thread inspection
- Xcode Memory Graph
- Instruments Leaks
- Instruments Allocations
- Hang analysis
- Time Profiler
- Early diagnostics and runtime checks
The codebase should eventually be organized around reusable lab concepts rather than one-off screens.
Possible structure:
SignalLabApp/Labs/Shared/Docs/Guides/
Potential shared concepts:
LabCatalogLabScenarioLabCategoryInvestigationGuideBrokenImplementationFixedImplementation
The exact implementation may evolve, but the project should preserve strong separation between shared infrastructure and lab-specific scenario code.
The app should eventually support:
- A home screen with all labs grouped by category or difficulty
- Rich lab detail screens with overview, trigger controls, and hints
- A clear broken/fixed mode toggle
- Investigation guides that explain which tool to use first and why
- Visual summaries of symptoms, tool recommendations, and validation steps
- A polished, modern interface that feels at home on Apple platforms
SignalLab should be documented like a real teaching product.
Recommended documentation areas:
- Project vision and roadmap
- Lab design principles
- Investigation workflow checklists
- One guide per lab
- Contributor guidance for adding future labs
- Notes for mentors or workshop facilitators
Documentation should explain not only how to use the app, but also why each scenario was designed the way it was.
SignalLab is successful if a learner can:
- Launch a lab and reproduce the issue quickly
- Choose an appropriate first debugging tool
- Gather evidence from the debugger or Instruments
- Explain the root cause in simple terms
- Understand why the fix resolves the issue
- Build confidence debugging similar problems in real projects
SignalLab is now a working iOS lab app with the MVP curriculum implemented and additional diagnostics/Phase 2 labs available in the catalog.
Work completed so far includes:
- Shared SwiftUI app shell and lab detail scaffold
- Working catalog navigation for MVP, diagnostics, and Phase 2 labs
- MVP labs for crash debugging, exception breakpoints, line breakpoints, Memory Graph, hangs, and CPU hotspots
- Post-MVP diagnostics labs for Thread Performance Checker, Zombies, Thread Sanitizer, and Malloc Stack Logging
- Phase 2 labs for heap growth, deadlocks, background-thread UI work, main-thread I/O, scroll hitching, startup signposts, and concurrency isolation
- Memory Graph Lab at MVP quality with the Open Note fixture, Malloc Stack Logging scheme support, and allocation-backtrace workflow
- Generated SignalLab app icon installed in the Xcode asset catalog
The next major step is a PR review pass focused on consistency, screenshots, and any remaining lab evidence gaps before merging the Memory Graph MVP work.
SignalLab is intended to complement Apple’s debugging and performance tooling documentation, not replace it.
Helpful Apple references include:
- Instruments tutorials
- Xcode hang analysis guidance
- Memory, thread, and crash diagnostics guidance
- WWDC sessions on heap analysis, hang investigation, and debugging tools
As the project grows, new labs should only be added if they meet the product’s teaching standards.
A good new lab should:
- Have a clear primary lesson
- Reproduce consistently
- Use realistic code patterns
- Teach a meaningful Apple debugging workflow
- Include a clear fixed implementation or explanation
- Be understandable by the intended audience
SignalLab is a polished, educational debugging lab for iOS developers.
Its purpose is to make Xcode debugging and Instruments feel practical, approachable, and memorable by teaching through direct investigation of realistic bugs.
The current focus is making the MVP labs concise, reproducible, and consistent enough to teach well. From there, the project can continue expanding into a broader curriculum covering more advanced debugging scenarios.