Skip to content

Add FrozenChildObservationTests: live-vs-frozen observation contract#27

Merged
mansbernhardt merged 2 commits into
mainfrom
claude/frozen-child-observation-tests
Jun 22, 2026
Merged

Add FrozenChildObservationTests: live-vs-frozen observation contract#27
mansbernhardt merged 2 commits into
mainfrom
claude/frozen-child-observation-tests

Conversation

@mansbernhardt

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #26 (merged), which fixed the optional/container same-id unanchored-child bug and exposed public var Model.modelID. This PR adds the regression/characterisation tests for the underlying observation contract — the why behind the "SwiftUI sub-view stuck at a child's birth state" field symptom. Tests only; no source changes.

FrozenChildObservationTests pins down:

  1. A child read out of a live parent is itself live (context != nil, lifetime == .active) and tracks subsequent mutations — the normal read path never hands a frozen copy to a sub-view.
  2. A frozen copy (same modelID, context == nil) silently does not participate in Apple's withObservationTracking — the iOS 17+ registrar path SwiftUI drives invalidation through — so a view holding one never re-renders. This is the precise mechanism that makes a non-live value render birth state forever, and the reason such a value must never reach an @ObservedModel.
  3. Per-instance identity via the public modelID (added in Fix unanchored optional/container child on same-id replace; expose modelID #26) distinguishes two distinct instances that reuse the same explicit domain id (where Identifiable.id alone cannot).

Validation

scripts/test --filter SwiftModelTests.FrozenChildObservationTests — all 4 tests pass against current main (post-#26). No source changes, so no behavior risk.

🤖 Generated with Claude Code

mansbernhardt and others added 2 commits June 20, 2026 09:40
Characterises why a non-live @model value silently fails to drive a SwiftUI
view — the mechanism behind the "sub-view stuck at a child's birth state"
symptom whose framework fix (optional/container same-id unanchored child)
landed in #26.

- A child read out of a live parent is itself live (context != nil,
  lifetime == .active) and tracks subsequent mutations — the normal read path
  never hands a frozen copy to a sub-view.
- A frozen copy (same modelID, no context) does NOT participate in Apple's
  withObservationTracking (the iOS 17+ registrar path SwiftUI uses), so a view
  holding one never re-renders. Hence a non-live value must never reach an
  @ObservedModel.
- Also covers reading per-instance identity via the public `modelID` (added in
  #26) when an explicit domain `id` shadows Identifiable.id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bservation-tests

# Conflicts:
#	CHANGELOG.md
@mansbernhardt mansbernhardt merged commit f30c030 into main Jun 22, 2026
6 checks passed
@mansbernhardt mansbernhardt deleted the claude/frozen-child-observation-tests branch June 22, 2026 13:15
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.

1 participant