agentsclimarketplace

Ios performance profiler

Skill Desquared/agents-rules-skills/skills/ios-performance-profiler

Identifies potential performance bottlenecks in SwiftUI code — expensive view body computation, unnecessary redraws, identity instability, concurrency issues, and memory problems. Use for lists, animations, complex layouts, remote data, or when the user asks about performance.From its SKILL.md

Install
npx -y skills add Desquared/agents-rules-skills --skill ios-performance-profiler

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.8 KB, as published. Nobody here has run it

Performance Profiler

View Identity & Diffing

  • Stable, persistent identity (Identifiable with stable id, avoid random UUIDs in rows)
  • Prefer value types + structural identity over class reference identity when possible
  • Avoid .id() unless absolutely necessary (use stable keys instead)

Body Computation & Redraws

  • No heavy work (network, decoding, sorting, filtering) inside body
  • Expensive derived state computed once and stored (.onAppear, ViewModel, or memoized computed property)
  • @State, @Bindable, @Observable properties are narrowly scoped
  • Child views extracted when parent state changes frequently
  • Prefer @Observable (Observation framework) over @ObservableObject / @Published for new code — finer-grained invalidation, less boilerplate

Lists & Large Collections

  • Use List / LazyVStack / LazyHGrid instead of ScrollView + ForEach + VStack
  • Rows are lightweight; avoid @StateObject / @ObservedObject inside row bodies
  • Use stable Identifiable conformance or explicit id: key path

Concurrency & Main Actor (Swift 6 era)

  • @MainActor used appropriately on view-bound types (avoid overuse → thread hops)
  • Nonisolated properties / computed vars when safe
  • Actors used for model / service layers; avoid @MainActor on pure data models
  • No data races when using @Observable types across concurrency domains
  • Prefer .task over .onAppear for async work (automatic cancellation on view disappear, structured concurrency)

Environment & State Propagation

  • Avoid injecting large @Observable objects via @Environment when only a small slice of state is needed — extract a child view that reads only what it needs
  • Minimize over-subscription from EnvironmentObject — split into focused, smaller observable types if necessary

Memory & Retain Cycles

  • [weak self] in async / escaping closures that capture self
  • @StateObject for view-owned view models (not in List rows)
  • Remote images use proper caching (AsyncImage with cache, or Kingfisher / Nuke)
  • Downscale images to display size before rendering (.resizable() + .frame() alone doesn't reduce memory)
  • Use preparingThumbnail(of:) for large images
  • Avoid long-lived strong references in @Observable / @ObservableObject

Layout & Geometry

  • Minimize GeometryReader usage inside scroll views (causes layout thrashing)
  • Avoid PreferenceKey updates that trigger parent re-layouts in tight loops
  • Use drawingGroup() for complex vector paths / heavy Canvas content

Animations & Transitions

  • Scope animations narrowly (.animation(…, value: …)) instead of global .animation()
  • Use .matchedGeometryEffect only when necessary (expensive)
  • Test with reduced motion enabled

Navigation & Sheets

  • Use lazy destination resolution in NavigationStack (avoid pre-building destination views)
  • Ensure .sheet / .fullScreenCover content isn't computed until presentation
  • Avoid deep view hierarchies in destinations — flatten or split into smaller views

Quick Wins

IssueFix / PatternSeverity
Parent redraws child unnecessarilyExtract stable child view / use Equatable conformance🟡
Heavy logic in bodyMove to ViewModel / .task / computed property🔴
Unstable ForEach IDsUse stable id: \.self or persistent model ID🔴
Retain cycle\[weak self\] in closures🔴
@StateObject recreated in List rowMove ownership to parent / use @Observable value type🔴
Broad / unnecessary animations.animation(nil) or value-specific .animation(…, value:)🟡
Frequent main-actor hopsUse nonisolated properties, actors for model layer🟡
GeometryReader in scroll viewsMove geometry reads outside scroll or cache values🟡
Large images not downscaledpreparingThumbnail(of:) or resize before display🟡
Over-subscribed @EnvironmentExtract child view reading only needed state🟡
Eager sheet / destination initLazy destination resolution in NavigationStack🟡
Still using @ObservableObjectMigrate to @Observable (Observation framework)🟢
Complex vector / Canvas pathsUse drawingGroup() for off-screen rendering🟢

Debug Helpers (Xcode 16+)

// In any View body — logs when/why view redraws
let _ = Self._printChanges()
// os_signpost for custom performance intervals
import os.signpost

let log = OSLog(subsystem: "com.app.performance", category: "ViewLoading")
let signpostID = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "LoadData", signpostID: signpostID)
// ... work ...
os_signpost(.end, log: log, name: "LoadData", signpostID: signpostID)

Instruments & Tools

  • SwiftUI template + Animation instrument in Instruments
  • SwiftUI View Body instrument (Xcode 16+) — tracks body evaluations per view
  • SwiftUI Performance HUD — ⌥⌘P in Simulator
  • MetricKit / MXMetricManager — production performance & diagnostic data
  • Memory Graph Debugger — catch retain cycles and leaks at runtime

Severity Legend

🔴 Critical — Visible lag, dropped frames, memory growth, crashes

🟡 Moderate — Noticeable jank on low-end devices or large datasets

🟢 Minor — Optimization opportunity / future-proofs for larger scale

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.