Skip to content
Back to skills

Software Ios Runtime Debugging

ASecurity

Proves iOS build/install/launch truth and triages hangs, crashes, jank, memory kills, and stale builds. Use when simulator, bundle, or runtime performance state is in doubt.

  • 89 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsrustgoswiftbashtestingdebugginggitapibackendsecurity

Works with

  • claude code
  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 11 files and shows the line behind each finding

Scanned October 6, 2026

npx -y skills add vasilyu1983/AI-Agents-public --skill software-ios-runtime-debugging --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Software Ios Runtime Debugging?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Software Ios Runtime Debugging
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vasilyu1983-software-ios-runtime-debugging/badge)](https://www.skillsdirectory.com/skills/vasilyu1983-software-ios-runtime-debugging)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: software-ios-runtime-debugging
description: "Proves iOS build/install/launch truth and triages hangs, crashes, jank, memory kills, and stale builds. Use when simulator, bundle, or runtime performance state is in doubt."
compatibility: Portable core. Works on Claude Code and Codex.
version: "1.1"
last_validated: 2026-07-11
---

# Native iOS Runtime Debugging

Use this skill when the core problem is not app architecture or visual design, but runtime truth: did the current binary build, install, launch, and render on the intended simulator or device — and once that's proven, is the live complaint a hang, a crash, jank, a memory kill, or a launch-time regression?

This skill owns stale-build suspicion, simulator drift, malformed `.app` bundles, missing executables, XcodeGen resource packaging mistakes, and the proof loop required before trusting screenshots, UI behavior, or downstream API/auth debugging. It also owns classifying and diagnosing live runtime performance and stability complaints once that proof exists: hangs and watchdog kills, crash symbolication, jank against frame budgets, Jetsam/memory pressure, and launch-time measurement traps.

## Quick Reference

| Symptom | First Move | Notes |
|--------|------------|-------|
| Screenshot does not match source | Preserve repro, then replace/install and launch the known artifact | Reset or uninstall only after preserving evidence and isolating the suspected layer |
| Tool cannot read a simulator screenshot temp path | Re-capture from the current simulator | Temp screenshot files expire or move. Do not treat a missing temp path as invalidating the user's visible report |
| Install says app is missing executable | Inspect built `.app` bundle | Verify `Info.plist` and executable path before touching Swift |
| Simulator behaves inconsistently | Prove destination, boot, install, launch state | Do not debate UI until runtime truth exists |
| Auth appears to succeed but next screen is unauthenticated | Inspect token persistence and auth propagation after fresh launch | Do not redesign UI first |
| Push works on Xcode build but fails on TestFlight | Inspect archived entitlements and newest backend device row | Wrong APNs environment is more likely than feature-code regression |
| APNs returns `BadDeviceToken` for older installs | Check device-row environment and staleness first | Often stale tokens or env mismatch, not a current-device blocker |
| Push tap opens to black screen, freeze, or `_performBlockAfterCATransactionCommitSynchronizes` | Start at [`references/swift-concurrency-crash-triage.md`](references/swift-concurrency-crash-triage.md); web-search the exact symbol before any code review | Root-cause candidates include a nonisolated delegate or completion callback mutating main-actor UI state, nested actor hops, or SceneKit updates. `Task { }` inherits the actor context where its closure is formed; inspect that creation context rather than assuming every task is detached. Separate transport proof from push-open proof |
| UITest env var is present but the wrong screen is captured | Verify branch execution and a screen-specific accessibility marker | Process env alone is not runtime proof |
| Route state changes but the destination never appears | Prefer a direct presentation hook for isolated proof | `NavigationStack` state is not the same as a visible screen |
| "cannot find X in scope" after adding new files | XcodeGen project not regenerated | Regenerate: `scripts/generate-xcodeproj.sh` or `xcodegen generate` |
| "cannot find X in scope" in a `.pbxproj` project after adding files | New file not added to target membership | Add the file to the target before touching Swift feature code |
| CoreSimulatorService "Connection refused" | Simulator service crashed | Use `generic/platform=iOS` destination instead of simulator; or restart Simulator.app |
| DerivedData write failure / sandbox error | Check the denied path and active toolchain | Use a writable project-specific DerivedData path; if platform services need wider access, use the runtime's scoped approval mechanism or Xcode.app |
| Swift "failed to produce diagnostic" | Type inference overload in complex ViewBuilder | Simplify: inline optional views, remove `AnyView`, split large computed properties |
| "Copy Bundle Resources contains entitlements" warning | Check whether the entitlements file was added as a bundled resource | Keep entitlements in signing configuration only; exclude them from copied app resources |
| **Background `xcodebuild … \| tail -N` output file stays 0 bytes until exit** | Looks like a stall; not one | `tail` emits only when its input stream closes. In background execution, the output file appears empty for the entire build run, which looks stuck but is actually normal. Alternatives: `2>&1 \| tee output.log` for live progress, or drop `tail` entirely and accept the full output. The `tail -N` recipe trades live visibility for clean final output — pick based on whether you need progress signals during the run |
| `Ld failed` / missing file after project generation | Check generated paths and target membership, then try an isolated clean build | Cache drift is a hypothesis; a missing source or resource remains missing after a cache reset |
| App terminates with `0x8badf00d` / `WATCHDOG` reason | Not a code crash — a callback (scene-create, background task) failed to return in time | Read the reason string for which subsystem timed out; treat as a hang that ran out the clock. See [references/runtime-performance-triage.md](references/runtime-performance-triage.md#hangs-and-watchdog-terminations) |
| Process still alive but input goes unanswered | Hang, not crash — no crash log will exist | Capture a main-thread backtrace via the Hangs instrument or lldb `bt all`; do not search for a nonexistent crash report |
| Scrolling or animation stutters but the app stays responsive | Jank — a missed frame budget, not a hang | Profile with Hitches/SwiftUI instrument, not the Hangs template. 60 Hz = 16.67 ms/frame, ProMotion 120 Hz = 8.33 ms/frame (adaptive; measure the target device's active refresh rate) |
| App disappears with no crash log after memory growth | Suspect a Jetsam kill | Confirm via a jetsam event report or the memory diagnostic available in the target SDK; do not assume a normal crash was swallowed. Apple publishes no official per-device memory-limit table — treat any specific MB figure as empirical |
| "Main thread blocked" in a trace, but the code path looks fine | Possible priority inversion, not main-thread overwork | Check the QoS of every thread in the backtrace before moving work off main; a low-priority thread holding a lock the main thread needs looks identical to a slow main-thread task |
| Launch-time regression only shows up in some samples | Prewarming skew | The OS may prewarm the process before the user taps the icon — loads linked libraries, then suspends before any app code runs. No documented API detects it. Treat a single launch sample as unverified — use MetricKit's launch-type-bucketed metrics or a large field sample |
| MetricKit payload never arrives during local testing | Distinguish field delivery from simulated payloads | On a physical device, Xcode's Debug > Simulate MetricKit Payloads tests report handling with sample data; it does not measure the app's performance |

## When to Use This Skill

Use this skill to:

- Prove the current binary builds, installs, and launches on the intended target
- Diagnose stale installs or stale screenshots in simulator-driven workflows
- Inspect built `.app` bundles when installation fails
- Debug simulator boot, shutdown, destination, and launch-state drift
- Investigate XcodeGen, resource packaging, bundle executable, or `Info.plist` path problems
- Establish runtime truth before routing to feature implementation, design, or test skills
- Classify a live complaint as a hang, a crash, jank, a memory (Jetsam) kill, or a launch-time regression before choosing a fix
- Read Instruments, MetricKit, or crash-symbolication output and judge whether a lab fix will actually move field metrics

## Core Workflow

1. Discover the project entrypoint: workspace or project, scheme, configuration, destination, and bundle ID.
2. Check the callable tool inventory against Agent Tool Selection; record the selected Xcode/toolchain and destination.
3. Build the app with the simplest reproducible command.
4. Inspect the built `.app`:
   verify `Info.plist`, executable name, and expected bundle contents.
5. Preserve the reproduction before resetting anything: record launch arguments, deep link, account, local data dependency, installed bundle version/signature, logs, and the visible state.
6. Install or upgrade the freshly built bundle while preserving its data container where the target supports that path. Uninstall/reset only when replacement fails, signing differs, migration/state corruption is the suspected layer, or the repro evidence has already been captured.
7. Launch the freshly installed app and capture proof:
   screenshot, UI hierarchy, launch logs, and a target-screen-specific marker when isolating a route.
8. Only after the app is freshly running, debug feature behavior, design, auth, or API issues.
   - For push issues, also prove the binary origin (Xcode debug vs TestFlight), the signed APNs entitlement on archive builds, and the newest backend device-row environment before chasing app logic.
- Treat transport proof and push-open proof as separate gates: APNs success and a visible banner do not prove tapping is safe.
9. Route onward:
   - visual hierarchy and HIG review -> [software-ios-design](../software-ios-design/SKILL.md)
   - native feature or architecture work -> [software-ios-native](../software-ios-native/SKILL.md)
   - test execution and `xcresult` triage -> [qa-testing-ios](../qa-testing-ios/SKILL.md)

## Runtime Proof Loop

- Prefer one bounded loop:
  discover -> build -> inspect bundle -> preserve repro -> replace/install -> launch -> capture evidence
- Escalate separately to a clean build, container reset, or uninstall. Record which reset changes the symptom; that difference distinguishes build drift from persisted-state or migration defects.
- If any step fails, stop there and fix that layer before moving deeper.
- Do not trust screenshots from a simulator session that has not been tied to the current build.
- Do not trust “build succeeded” on its own; install and launch proof still matter.
- Do not stop on an unreadable temp screenshot path. Re-capture a screenshot, inspect the UI tree, or use the user's exact visible symptom to drive a focused source-level check.
- Verify isolated launch hooks at three levels: the env reached the process, the intended app branch executed, and the target screen is present through a screen-specific accessibility marker.
- If a screenshot or UI tree contradicts the expected launch hook, inspect only the named non-secret launch-hook flag values through a scoped app diagnostic and then verify the marker before trusting the capture.

## Agent Tool Selection

Choose the smallest callable tool that covers the evidence needed. A documented tool is not proof that this runtime exposes it.

| Need | Default | Capability check / fallback |
|---|---|---|
| Build through an open Xcode project | Apple's Xcode MCP bridge | Check Xcode Intelligence access and the connected tool inventory; Apple documents `xcrun mcpbridge`. Use CLI if the bridge is unavailable. [Apple guide](https://developer.apple.com/documentation/xcode/giving-external-agents-access-to-xcode) |
| Simulator build/install/launch/log/UI loop | Existing XcodeBuildMCP or MobileBuildMCP connection | Upstream is [MobileBuildMCP](https://github.com/getsentry/MobileBuildMCP); inspect the installed tool inventory and configuration rather than assume old tool names still work. Fall back to `xcodebuild` + `simctl`. |
| Paused-process stack, variables, stepping | LLDB or its MCP interface | [LLVM's `lldb-mcp`](https://lldb.llvm.org/use/mcp.html) requires a compatible installed debugger; verify availability and a stopped process. MCP debugger output excludes the debuggee's stdout. |
| Physical-device install/launch/control | `xcrun devicectl` | Use `xcrun devicectl help` for the installed command surface and check device/signing state. It does not replace symbolication or Instruments. [Apple CLI reference](https://developer.apple.com/documentation/xcode/xcode-command-line-tool-reference) |
| Result bundle or performance trace | `xcresulttool` or `xctrace` | Read installed help for result/trace schemas; keep raw artifacts alongside filtered output. |

Project scaffolding, buildable folders, warnings policies, and Xcode Cloud setup belong to [software-ios-native](../software-ios-native/SKILL.md). Server auth, writes, and deployment behavior belong to [software-backend](../software-backend/SKILL.md); request/response contracts belong to [dev-api-design](../dev-api-design/SKILL.md).

## Stale-Build Heuristics

| Symptom | Suspect | Action |
|---|---|---|
| UI doesn't match current source | Stale install | Preserve repro; replace/install the verified bundle |
| App shows old screen after rebuild | Cached install | Inspect artifact and install logs; replace before reset |
| Build succeeded but app looks old | Stale DerivedData or incremental build error | Check install logs; do not keep editing feature code |
| Simulator already running, UI state surprising | Previous simulator session | Re-prove install and launch before reasoning about app state |
| Push works on Xcode build, fails on TestFlight | APNs environment mismatch | Local → `sandbox`; TestFlight / App Store → `production`; verify per-device row |
| Transport works, app freezes or crashes on push tap | Notification-open path: delegate isolation, route staging, off-main mutation | Route to [swift-concurrency-crash-triage.md](references/swift-concurrency-crash-triage.md) |
| `dataCorrupted` + `<!DOCTYPE html>` response | API routing / auth bug | Log URL, curl it; do not conflate with push-open crashes |
| Route state updates but destination never appears | Presentation hook not reached | Use a direct presentation hook to prove the screen in isolation |
| Simulator unresponsive (CoreSimulatorService errors) | Simulator service crashed | Switch to `generic/platform=iOS` for compile-only verification |

See [references/stale-build-triage.md](references/stale-build-triage.md).

## Packaging and Bundle Health

- When installation fails, inspect the built `.app` bundle directly.
- Confirm:
  - `Info.plist` has expanded values, not unresolved placeholders
  - the executable exists at the path referenced by the bundle metadata
  - expected resources are copied as resources, not malformed folder references
- If the error mentions missing bundle executable, treat it as a packaging issue first.

See [references/xcodegen-resource-packaging.md](references/xcodegen-resource-packaging.md).

## Swift Concurrency Hop Rule

In a `@MainActor` context, code after an `await` resumes on the MainActor. Swift does not lose isolation across an `await` (SE-0338). Under the default settings, the awaited `nonisolated async` function itself runs off the main actor. If that function reaches main-actor state (possible through `@unchecked Sendable` or minimally checked code), that access is the bug; a missed hop back is not. Swift 6.2's approachable-concurrency setting, or the `NonisolatedNonsendingByDefault` upcoming feature (SE-0461), makes `nonisolated async` functions run on the caller's actor unless marked `@concurrent`. Check the project's build settings before reasoning about where a function runs. Crash patterns and fixes are in [references/swift-concurrency-crash-triage.md](references/swift-concurrency-crash-triage.md).

## Runtime Performance Triage

Once build/install/launch truth is established, a live complaint still needs to be classified before you touch code. Hang, watchdog kill, jank, Jetsam kill, and launch-time regression have different evidence and different fix ladders — do not default to "read the code" until the failure class has a name.

- Walk the triage order: terminated-with-crash-log → terminated-with-`0x8badf00d`/watchdog → alive-but-unresponsive (hang) → alive-and-responsive-but-stuttering (jank) → disappeared-with-no-crash-log (Jetsam) → slow-to-first-frame (launch).
- Watch for the two most common misdiagnoses: blaming "main thread blocked" when it's priority inversion on a lower-QoS thread holding a shared lock, and trusting a single launch-time sample that may have been prewarmed.
- Instruments, MetricKit, hang/watchdog thresholds, Jetsam behavior, frame-budget math, crash symbolication, LLDB workflows, thermal-state handling, and launch-time optimization are covered in depth in [references/runtime-performance-triage.md](references/runtime-performance-triage.md) — including which of these categories the Simulator cannot faithfully reproduce, and why lab evidence alone should never close out a field-facing performance fix.

## Archive And APNs Validation

- Validate the exact `.xcarchive` selected for upload before blaming runtime code. Avoid generic `find ... .app | tail -1` shortcuts when multiple archives or export folders may exist:

```bash
codesign -d --entitlements :- "$APP" 2>/dev/null
security cms -D -i "$APP/embedded.mobileprovision" | plutil -p - | grep -A2 aps-environment
```

- Pass condition for a TestFlight/App Store archive:
  - `aps-environment = production` in the archived app entitlements
  - `get-task-allow = false`
  - `aps-environment = production` in the embedded provisioning profile
- If `aps-environment = development` or `get-task-allow = true`, the archive is still development-signed. If those values are correct but validation fails, inspect generated plist metadata next.
- When push debugging crosses release channels, separate token-registration truth from transport truth. A common iOS/TestFlight failure is: many sandbox deliveries succeed, the only production delivery fails with `BadDeviceToken`, and the phone receives nothing. Treat that as a stale-registration or invalid-production-token problem first, not as proof that APNs transport or signing is generally broken.
- Preserve the device registration and failure response before resetting. Prove the installed build channel, current token registration, APNs environment, successful send, actual receipt, and push-open behavior separately. Route backend device-row lifecycle changes to software-backend; an app uninstall is a later diagnostic reset, not a default transport fix.

## XcodeGen and Project File Discovery

Generator specs and target membership cause install-time failures and "cannot find X in scope" errors that look like Swift bugs. Regenerate XcodeGen projects after adding files (`scripts/generate-xcodeproj.sh` or `xcodegen generate`), and add new files to the target in `.pbxproj`-managed projects. Details in [references/xcodegen-resource-packaging.md](references/xcodegen-resource-packaging.md#project-file-discovery).

## Route Elsewhere

- Use [software-ios-native](../software-ios-native/SKILL.md) once runtime truth is established and the task becomes feature implementation, rewrite planning, or SwiftUI architecture.
- Use [software-ios-design](../software-ios-design/SKILL.md) once the screen is confirmed to come from a fresh build and the task is visual hierarchy, typography, materials, or HIG compliance.
- Use [qa-testing-ios](../qa-testing-ios/SKILL.md) once the app is buildable and installable and the task becomes test execution, `xcresult`, destinations, or flake control.
- Use [software-mobile](../software-mobile/SKILL.md) for platform choice, Android, or cross-platform tradeoffs.

## Navigation

### References

| Resource | Purpose |
|----------|---------|
| [references/runtime-proof-loop.md](references/runtime-proof-loop.md) | Canonical build/install/launch verification loop |
| [references/stale-build-triage.md](references/stale-build-triage.md) | Heuristics for screenshots, stale installs, and simulator drift |
| [references/xcodegen-resource-packaging.md](references/xcodegen-resource-packaging.md) | XcodeGen and bundle-packaging failure patterns |
| [references/swift-concurrency-crash-triage.md](references/swift-concurrency-crash-triage.md) | Symptom-first triage for concurrency-rooted crashes and freezes |
| [references/runtime-performance-triage.md](references/runtime-performance-triage.md) | Hang/crash/jank/memory/launch triage tree, Instruments, MetricKit, Jetsam, frame budgets, thermal state |
| [data/sources.json](data/sources.json) | Primary Apple and XcodeBuildMCP sources |

### Templates

| Template | Purpose |
|----------|---------|
| [assets/template-ios-runtime-debug-request.md](assets/template-ios-runtime-debug-request.md) | Short request format for proof-first runtime debugging |

### Related Skills

| Skill | Purpose |
|-------|---------|
| [software-ios-native](../software-ios-native/SKILL.md) | Native iOS implementation and rewrites after runtime truth exists |
| [software-ios-design](../software-ios-design/SKILL.md) | Visual audits after fresh build/install/launch proof |
| [qa-testing-ios](../qa-testing-ios/SKILL.md) | XCTest, XCUITest, `xcresult`, and flake control after installability is proven |
| [software-mobile](../software-mobile/SKILL.md) | Mobile platform choice and cross-platform tradeoffs |

- Prefer Apple documentation for `xcodebuild`, `simctl`, and bundle structure behavior.
- Prefer upstream XcodeBuildMCP docs for tool names, CLI commands, and config keys.
- Treat repo-specific build, scheme, bundle ID, and generator behavior as local facts that must be discovered, not assumed.
- Instrument names, hardware-gated feature availability (e.g., Processor Trace chip requirements), and exact watchdog timings change across Xcode/iOS releases — re-verify against current Apple release notes rather than trusting a fixed number from this skill. Jetsam memory-limit figures are explicitly empirical, not Apple-published, and should be re-derived from device behavior (`os_proc_available_memory`, jetsam event reports), not hardcoded.

## Learnings Loop

When prior decisions or pitfalls are relevant, consult `learnings.consolidated.md` if present; use `learnings.md` only for needed history or as the available fallback. Otherwise skip both.

After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to `learnings.md` via `agents-skills-feedback-loop/scripts/append_learning.py`. Do not modify `SKILL.md` itself.

Files in this skill

  • SKILL.md28 KB
  • agents/openai.yaml603 B
  • assets/template-ios-runtime-debug-request.md593 B
  • data/sources.json6.8 KB
  • learnings.consolidated.md606 B
  • learnings.md1.5 KB
  • references/runtime-performance-triage.md20.4 KB
  • references/runtime-proof-loop.md645 B
  • references/stale-build-triage.md4.4 KB
  • references/swift-concurrency-crash-triage.md22.7 KB
  • references/xcodegen-resource-packaging.md737 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.