Skip to content
Development
Command

/perf-investigate

**Project context:** Values in angle brackets below (e.g. `<scheme>`, `<JIRA_KEY>`, `<flag-key-enum>`) are resolved at runtime — detect them from the project (`xcodebuild -list -json` for the scheme, `git`/`gh` for repo & owner, the branch name for the Jira key, a codebase

From plugin
ios-workflow-claude
722 skills3 agents22 commands
Install
$ npx -y skills add carloshpdoc/ios-workflow-claude --agent claude-code

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/perf-investigate

Context preview

What this command does when you run it.

**Project context:** Values in angle brackets below (e.g. `<scheme>`, `<JIRA_KEY>`, `<flag-key-enum>`) are resolved at runtime — detect them from the project (`xcodebuild -list -json` for the scheme, `git`/`gh` for repo & owner, the branch name for the Jira key, a codebase

Command definition

perf-investigate.md

Perf / Leak Investigation

> **Project context:** Values in angle brackets below (e.g. `<scheme>`, `<JIRA_KEY>`, `<flag-key-enum>`) are resolved at runtime — detect them from the project (`xcodebuild -list -json` for the scheme, `git`/`gh` for repo & owner, the branch name for the Jira key, a codebase search for flag/font files), or ask if they cannot be inferred. This plugin ships no per-project config.

Drive an iOS performance or memory-leak investigation on <scheme> using Memory Graph + Time Profiler. Enforce data-before-architecture and stacked PRs.

Usage

/perf-investigate <symptom or <JIRA_KEY>-XXXX>

**Examples:**

/perf-investigate <Feature> place-details opens are slow after 15 cycles
/perf-investigate <JIRA_KEY>-XXXX
/perf-investigate Explore tab leaks BSPlaceDetailVMV3

Triggers

Run this skill when the user mentions any of: "perf", "lento", "slow", "leak", "memory", "vazamento", "TTI", "hang", "jank", "frame drop", "spin", "freezou", "trava".

Hard rules (do not skip)

1. **No architectural change before measurement.** Reject hypotheses framed in architectural terms ("the wrapper is wrong", "the navigation pattern leaks") until the user provides a `.memgraph` (for retention) or a `.trace` (for TTI/jank). 2. **Memory Graph (`.memgraph`) for retain cycles, NOT `xctrace --template Leaks --attach`.** The latter fails silently with `libmalloc not initialized`. See `reference_ios_leak_tooling.md`. 3. **Time Profiler with `--attach` works** for hangs and CPU sampling. Use it for TTI/slowness investigations. 4. **Compare to a fluid baseline tab.** Pick a known-fluid feature in your app as the baseline; capture both the suspect and baseline in the same session. 5. **One PR per logical fix.** If the investigation produces 2+ fixes, use stacked PRs — see `commit-and-pr` skill.

Workflow

Phase 1 — Triage (no code yet)

1. Restate the symptom in observable terms (e.g., "after 15 opens, next open takes 6s" not "the wishlist is slow"). 2. Decide the right tool:

  • "X is retained / leaking / accumulating" → Memory Graph (Tool 2 in the tooling memory).
  • "X is slow / hangs / janky" → Time Profiler (Tool 3 in the tooling memory).
  • "Memory grows over time without leaks" → Allocations Mark-Heap (Instruments GUI only — fall back to user).

3. Ask the user to capture the right artifact and save to `~/Desktop/<descriptive-name>.{memgraph,trace}`. Provide the exact `xctrace` command if Time Profiler is needed. 4. Also ask for a baseline capture from a comparable fluid tab (usually Explore) when relevant.

Phase 2 — Analysis (CLI only, no GUI)

For Memory Graph (`.memgraph`):

leaks ~/Desktop/X.memgraph 2>&1 | grep -E "ROOT CYCLE|<TargetClass>" | head -40
heap ~/Desktop/X.memgraph 2>&1 | grep -E "BSPlaceDetailVMV3|<class names>"

For Time Profiler (`.trace`):

xcrun xctrace export --input ~/Desktop/X.trace \
  --xpath '/trace-toc/run/data/table[@schema="potential-hangs"]' \
  > /tmp/hangs.xml
xcrun xctrace export --input ~/Desktop/X.trace \
  --xpath '/trace-toc/run/data/table[@schema="time-profile"]' \
  > /tmp/time-profile.xml

Use the Python parser pattern at `/tmp/tti-analysis/analyze3.py` (or write a fresh one) to:

  • Filter to Main Thread samples
  • Group by binary, then by inclusive frame in the `<scheme>` binary
  • Print top-20 hot frames

Phase 3 — Identify root cause

Look for these patterns first (based on past iOS investigations):

| Pattern | Hint | |---|---| | `*Context.make` factory NOT going through a shared ViewModel cache while siblings use it | Compare entry points in your `<Feature>Factory.swift`: outlier features bypass the cache and re-create ViewModels on every push. Tracked as <JIRA_KEY>-XXXX. | | `CTTelephonyNetworkInfo.__allocating_init` in top-N inclusive | Per-instance network/connectivity checker being re-allocated. Promote to a `.shared` singleton (<JIRA_KEY>-XXXX). | | ROOT CYCLE through `TagIndexProjection<Int>` in a SwiftUI carousel | `.tag()` on ForEach items, closures capturing `self`. Convert closures to `static func` + `[weak]` (<JIRA_KEY>-XXXX). | | ROOT CYCLE involving `_ContiguousArrayStorage<Optional<...Coordinator...>>` or `SwiftUI.StoredLocation<Coordinator>` | `@State` reference type leaked via closure capturing `_state` wrapper. Use `[weak coord = self.coordinator]` not `[coordState = _coordinator]`. | | Cycle involving `UINavigationController.viewControllers ↔ host VC ↔ @State coordinator` | UIViewControllerRepresentable with fresh nav. Add `dismantleUIViewController` clearing `viewControllers`. |

If none match, document the new pattern at the end of this skill so future runs benefit.

Phase 4 — Fix and validate

1. Apply the smallest possible fix targeting only the proven cycle/hot-frame. 2. Build via `xcodebuild -scheme <scheme> -destination 'platform=iOS Simulator,name=iPhone 11,OS=latest' build`. 3. Ask user to capture a fresh `.memgraph` / `.trace` after reproducing the same flow. 4. Re-run Phase 2 analysis. Compare to baseline. 5. Goal: target hot frame drops by ≥50% OR retain count drops to 0.

Phase 5 — Output

Produce a comparison table for the PR description and the Jira comment using this template:

| Metric | BEFORE | AFTER | Baseline (Explore) |
|---|---|---|---|
| Hangs >250ms | N | M | E |
| Total hang time | Ns | Ms | Es |
| `<HotFrame>` inclusive | N% | M% | E% |

Then:

  • Open PR via `create-pr-from-staged-changes` skill, base `dev` (or stack on parent).
  • Update Jira via `update-jira` skill with the validation table embedded as a comment.
  • If the fix took fewer steps than originally sized, reduce the SP count on the ticket.

Anti-patterns (block these)

  • **Wholesale `[weak self]` refactor across SwiftUI body.** Capturing `_state` / `_observed` property wrappers can paradoxically retain backing storage in escaped closures. Only weak-capture the closure proven by the memgraph to be the cycle root.
  • **Migrating presentation pattern (`fullSc
Read more
Ships withios-workflow-claude

Reusable Claude Code slash-commands, skills, and workflows extracted from real iOS / backend projects. Packaged as three installable plugins - register the marketplace and /plugin install what you need.

Get the whole plugin, auto-invoked
Stats
7
Stars
0
Views
1
Forks
Maintained
Maintenance
Shell
Language
Apache-2.0
License
2mo ago
Last commit
2mo ago
Created

Repo: carloshpdoc/ios-workflow-claude

Other commands on ios-workflow-claude.