Skip to content

Merge learnings from Apple's Xcode 27 swiftui-specialist skill - #70

Merged
AvdLee merged 2 commits into
mainfrom
merge-apple-swiftui-specialist
Jun 16, 2026
Merged

Merge learnings from Apple's Xcode 27 swiftui-specialist skill#70
AvdLee merged 2 commits into
mainfrom
merge-apple-swiftui-specialist

Conversation

@AvdLee

@AvdLee AvdLee commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

Folds the strongest guidance from Apple's bundled Xcode 27 swiftui-specialist skill into our swiftui-expert-skill, aiming for the best of both: our breadth (layout, navigation, charts, macOS, previews, Instruments traces) plus Apple's depth on the invalidation model. Overlaps were de-duplicated with cross-links, and a few stale/conflicting points were corrected.

Fixes / corrections

  • list-patterns: .enumerated() no longer needs an Array(...) wrapper on Swift 6.1+, and the id must be the element's identity (\.element.id), not \.offset — the old advice was the same position-as-id trap as .indices.
  • list-patterns: the "unified row" example is now unary (branching wrapped in a container) so List can template row ids.
  • view-structure: softened the property-ordering mandate to an optional readability suggestion, per our own AGENTS.md (no formatting/linting rules).

Sharpened

  • view-structure: invalidation-boundary framing for extracting subviews; multi-section detail-view anti-pattern; "don't auto-refactor an existing .if during unrelated work" review note.
  • performance-patterns: full rapidly-updating-environment pattern (coarsened @Observable thresholds, per-item models, scrollTransition/visualEffect).
  • animation-advanced: when to implement animatableData manually; AnimatableValues (iOS 26+) vs AnimatablePair.

Added

  • state-management: make @Observable property types Equatable (setter short-circuit), per-property observation granularity traps + fixes, KeyPath bindings over closure bindings, no closures in custom environment keys, stable @Entry defaults, removing unused @Environment reads.
  • view-structure: keep view init cheap; single-child Group anti-pattern.
  • list-patterns: unary vs multi rows, -LogForEachSlowPath, stable/cheap/outliving ids.
  • New references/localization.md and references/soft-deprecation.md.
  • SKILL.md: Topic Router rows, References list, Correctness Checklist, and trigger description updated for the new coverage.

Notes / follow-ups

  • The deprecated-API data in references/latest-apis.md was not hand-edited; refreshing it against the iOS 27 SDK should be run through the existing .agents/skills/update-swiftui-apis maintenance skill (Sosumi-driven) as a separate change. A behavioral cross-reference to the new soft-deprecation.md was added.
  • All guidance was checked against AGENTS.md (SwiftUI-only, factual not architectural, optimizations framed as suggestions).

Test plan

  • Skim git diff for tone/accuracy and confirm no contradictions with existing sections
  • Verify all references/*.md links in SKILL.md resolve (they do)
  • Confirm TOC anchors match the new headings in state-management.md and view-structure.md
  • Optional: run the skill-creator description-optimization loop on the updated SKILL.md description

AvdLee and others added 2 commits June 16, 2026 09:42
…alist skill

Fold the strongest guidance from Apple's bundled swiftui-specialist skill into
the SwiftUI Expert Skill, deduplicating overlaps and fixing stale advice.

Fixes / corrections:
- list-patterns: `.enumerated()` no longer needs Array() on Swift 6.1+, and the
  id must be the element's identity, not `\.offset` (same trap as `.indices`)
- list-patterns: make the unified-row example unary (wrap branching in a
  container) so List can template row ids
- view-structure: soften the property-ordering mandate to an optional
  readability suggestion (per AGENTS.md, no formatting rules)

Sharpened:
- view-structure: invalidation-boundary framing for extracting subviews, plus
  the multi-section detail-view anti-pattern and a "don't auto-refactor .if"
  review note
- performance-patterns: full rapidly-updating-environment pattern (coarsened
  @observable thresholds, per-item models, scrollTransition/visualEffect)
- animation-advanced: when to implement animatableData manually, AnimatableValues
  (iOS 26+) vs AnimatablePair

Added:
- state-management: make @observable property types Equatable (setter
  short-circuit), per-property observation granularity traps + fixes, KeyPath
  bindings over closure bindings, no closures in custom environment keys,
  stable @entry defaults, removing unused @Environment reads
- view-structure: keep view init cheap; single-child Group anti-pattern
- list-patterns: unary vs multi rows, -LogForEachSlowPath, stable/cheap ids
- new references/localization.md and references/soft-deprecation.md
- SKILL.md Topic Router, References list, Correctness Checklist, and trigger
  description updated for the new coverage

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Integrates and de-duplicates guidance from Apple’s Xcode 27 swiftui-specialist skill into this repo’s swiftui-expert-skill, expanding coverage around invalidation/observation, list identity, and localization while adding new reference docs and cross-links.

Changes:

  • Expanded the skill router/checklists and refreshed guidance across view structure, state management, lists, performance, and animations.
  • Added new reference docs for localization and soft-deprecation behavior, plus cross-references from existing docs.
  • Updated repository documentation to reflect the expanded reference set.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
swiftui-expert-skill/SKILL.md Updates trigger description, topic router, and correctness checklist to include new/expanded areas (localization, soft-deprecation, list identity rules).
swiftui-expert-skill/references/view-structure.md Adds invalidation-boundary framing, review guidance around .if modifiers, and new sections on cheap init and single-child Group.
swiftui-expert-skill/references/text-patterns.md Adds a cross-link to the new localization reference to keep scope clear.
swiftui-expert-skill/references/state-management.md Adds new sections on @Observable granularity and environment key patterns (@Entry, closures, defaults), plus updated key principles.
swiftui-expert-skill/references/soft-deprecation.md New reference defining “soft-deprecation” behavior and scoping rules.
swiftui-expert-skill/references/performance-patterns.md Clarifies guidance around environment invalidation costs and introduces coarsening patterns for high-frequency values.
swiftui-expert-skill/references/localization.md New reference covering String Catalogs, bundles (packages/frameworks), formatting, RTL layout, and translator comments.
swiftui-expert-skill/references/list-patterns.md Updates list/ForEach identity guidance (unary rows, enumerated identity) and strengthens the checklist.
swiftui-expert-skill/references/latest-apis.md Adds a behavioral cross-reference to soft-deprecation guidance and the maintenance skill.
swiftui-expert-skill/references/animation-advanced.md Adds guidance for when to implement animatableData manually and references AnimatableValues vs AnimatablePair.
README.md Updates the reference structure listing to include newly added reference docs.

Comment on lines +372 to +387
Reach for an explicit `animatableData` (instead of the macro) when the interpolated value needs custom logic that doesn't map 1:1 to a stored property — normalization, clamping, or driving a derived value. For a deployment target of iOS 26+, use `AnimatableValues`; for earlier targets, use `AnimatablePair`.

```swift
// iOS 26+: keep phase in 0..<2π and clamp amplitude during interpolation
struct WaveShape: Shape {
var amplitude: CGFloat
var phase: CGFloat
var maxAmplitude: CGFloat

var animatableData: AnimatableValues<CGFloat, CGFloat> {
get { AnimatableValues(amplitude, phase) }
set {
amplitude = min(max(newValue.value.0, 0), maxAmplitude)
phase = newValue.value.1.truncatingRemainder(dividingBy: 2 * .pi)
}
}
Comment on lines 168 to +171
```

**No `Array(...)` wrapper is needed on Swift 6.1+.** As of Swift 6.1, the sequence returned by `.enumerated()` conditionally conforms to `RandomAccessCollection` when the base collection does, so `ForEach` accepts it directly. On earlier toolchains, wrap it in `Array(...)`. Favor the direct form in new code — it avoids an eager copy on every body evaluation.

Comment on lines 459 to 462
- [ ] No inline filtering in ForEach (prefilter and cache instead)
- [ ] No `AnyView` in list rows
- [ ] Don't convert enumerated sequences to arrays
- [ ] `.enumerated()` uses the element's id (not `\.offset`); no `Array(...)` wrapper needed on Swift 6.1+
- [ ] Use `.refreshable` for pull-to-refresh
Comment on lines 488 to +492
7. With `@Observable`, nested objects work fine; with `ObservableObject`, pass nested objects directly to child views
8. **Always add `@ObservationIgnored` to property wrappers** (e.g., `@AppStorage`, `@SceneStorage`, `@Query`) inside `@Observable` classes — they conflict with the macro's property transformation
9. **Prefer `Equatable` types for frequently-written `@Observable` properties** so the generated setter skips redundant invalidations
10. **Never store closures in custom environment keys; keep `@Entry` defaults stable** (no `Model()`/`Date()` expressions)
11. **Prefer KeyPath/subscript bindings over closure bindings**
Comment on lines +111 to +123
The `@Observable` macro generates a setter that **skips invalidation when the new value equals the current one** — but only when it can compare them, which means only when the property's type is `Equatable`. Without that conformance, every assignment notifies observing views, even when the value is identical. This is an easy win for properties written frequently with the same value (polling, streaming updates, timers).

```swift
// AVOID: not Equatable — every assignment invalidates, even no-op writes
enum DeliveryStatus { case placed, preparing, shipped, delivered }

// PREFER: Equatable lets the generated setter short-circuit redundant writes
enum DeliveryStatus: Equatable { case placed, preparing, shipped, delivered }
```

This applies to collection properties too: an `Array`/`Set`/`Dictionary` is only `Equatable` when its element type is, so a non-`Equatable` element defeats the short-circuit for the whole collection. (The check is emitted into the generated setter as user code, so it applies on every OS that supports `@Observable` when built with current Xcode.)

This is distinct from `Equatable` *views* (see `references/performance-patterns.md`): that conformance lets SwiftUI skip a view's body; this one lets the model skip notifying observers in the first place.
@AvdLee

AvdLee commented Jun 16, 2026

Copy link
Copy Markdown
Owner Author

Thanks @copilot — I went through all five comments. Each one challenges guidance that was ported verbatim from Apple's Xcode 27 swiftui-specialist skill, and in each case the flagged claim is intentional and correct against the current toolchain (the suggestions reflect pre-Swift-6.1 / pre-iOS-26 behavior). Leaving the text as-is. Details:

1. animation-advanced.mdAnimatableValues availability + component access
AnimatableValues is a new iOS 26 type (variadic generics) and exposes its components via .value. The .first/.second API and iOS 17+ availability belong to AnimatablePair, which the doc also covers for earlier deployment targets. Apple's source uses AnimatableValues<…> with newValue.value.0/.1 gated to deployment target ≥ 26.0.

2 & 3. list-patterns.md.enumerated() + RandomAccessCollection
The conditional Collection/RandomAccessCollection conformance on .enumerated() was added in Swift 6.1 (SE-0459, 2025), not 5.8. Before 6.1 the Array(...) wrapper is required, which is exactly what the section says. The checklist line is consistent with the prose intentionally.

4 & 5. state-management.md@Observable + Equatable setter short-circuit
The @Observable macro in current Xcode (Swift 6.2) emits an equality check directly into the generated setter for Equatable property types, so a no-op assignment skips invalidation. This is macro-generated user code, not a runtime feature, so it applies on every OS that supports @Observable (iOS 17+). The "notifies on every set" behavior predates this macro change.

Source of truth for all four areas is Apple's bundled swiftui-specialist skill (references/animations.md, references/foreach.md, references/dataflow.md).

@AvdLee
AvdLee merged commit 73aae6a into main Jun 16, 2026
1 of 2 checks passed
@AvdLee
AvdLee deleted the merge-apple-swiftui-specialist branch June 16, 2026 07:57
Copilot stopped work on behalf of AvdLee due to an error June 16, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants