Add fourteen skills from Dimillian/Skills, integrate the merged Snowflake and WordPress updates into the maintainer sync, and refresh registry metadata, attributions, walkthrough notes, and the 8.9.0 release notes while keeping validation warnings within budget.
1.9 KiB
1.9 KiB
Performance guardrails
Intent
Use these rules when a SwiftUI screen is large, scroll-heavy, frequently updated, or at risk of unnecessary recomputation.
Core rules
- Give
ForEachand list content stable identity. Do not use unstable indices as identity when the collection can reorder or mutate. - Keep expensive filtering, sorting, and formatting out of
body; precompute or move it into a model/helper when it is not trivial. - Narrow observation scope so only the views that read changing state need to update.
- Prefer lazy containers for larger scrolling content and extract subviews when only part of a screen changes frequently.
- Avoid swapping entire top-level view trees for small state changes; keep a stable root view and vary localized sections or modifiers.
Example: stable identity
ForEach(items) { item in
Row(item: item)
}
Prefer that over index-based identity when the collection can change order:
ForEach(Array(items.enumerated()), id: \.offset) { _, item in
Row(item: item)
}
Example: move expensive work out of body
struct FeedView: View {
let items: [FeedItem]
private var sortedItems: [FeedItem] {
items.sorted(using: KeyPathComparator(\.createdAt, order: .reverse))
}
var body: some View {
List(sortedItems) { item in
FeedRow(item: item)
}
}
}
If the work is more expensive than a small derived property, move it into a model, store, or helper that updates less often.
When to investigate further
- Janky scrolling in long feeds or grids
- Typing lag from search or form validation
- Overly broad view updates when one small piece of state changes
- Large screens with many conditionals or repeated formatting work
Pitfalls
- Recomputing heavy transforms every render
- Observing a large object from many descendants when only one field matters
- Building custom scroll containers when
List,LazyVStack, orLazyHGridwould already solve the problem