Skip to content

Feat: Align the data-flow wrappers with SwiftUI (DynamicProperty, Binding, Bindable, AppStorage)#69

Merged
phranck merged 11 commits into
mainfrom
issue/18-data-flow-alignment
Jul 22, 2026
Merged

Feat: Align the data-flow wrappers with SwiftUI (DynamicProperty, Binding, Bindable, AppStorage)#69
phranck merged 11 commits into
mainfrom
issue/18-data-flow-alignment

Conversation

@phranck

@phranck phranck commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Closes #18.

Summary

  • DynamicProperty: public protocol with default update(); the renderer hydrates user-defined dynamic properties recursively (nested framework wrappers bind depth-first with stable declaration-ordered indices) and runs update() once per hydration before body
  • Binding: @dynamicMemberLookup child bindings with writeback, init(projectedValue:), optional promotion/unwrapping, AnyHashable projection, conditional Identifiable + Sequence/Collection/BidirectionalCollection/RandomAccessCollection (writable element bindings), and @unchecked Sendable where Value: Sendable
  • State: init(initialValue:) and the empty initializer for nil-expressible values
  • AppStorage: struct-level Codable bound removed; SwiftUI's typed initializer families (Bool, Int, Double, String, URL, Data, Date, RawRepresentable String/Int + optional variants with remove-on-nil) delegate to family-specific read/write steps on the injected, ordered backend from [P0-10] Serialize AppStorage persistence and inject its backend per app #15; the backend parameter now uses SwiftUI's store label and accepts a StorageBackend as the documented terminal adaptation of UserDefaults; arbitrary Codable values stay as a documented additive convenience
  • Bindable: dynamic member lookup producing writable bindings into @Observable models, all three initializer spellings, conditional Identifiable
  • Sendability compile contracts recharacterized (Sendable-value bindings are Sendable; non-Sendable-value bindings stay rejected); data-flow fixture extended; docs updated; manifest/policy regenerated from fresh 6.0.3 snapshots

Deviations from the issue text (documented)

Test plan

  • 1366 tests green on macOS (Swift 6.0.3) and Linux (Docker 6.0.3-noble) via ./scripts/test-linux.sh, warning-fatal
  • New suites: DynamicPropertyTests, BindingAlignmentTests, StateAlignmentTests, AppStorageFamilyTests, BindableTests
  • verify-compatibility-manifest.sh passes against the regenerated manifest

phranck added 11 commits July 22, 2026 13:23
- Add the public DynamicProperty protocol with a default update() and
  conform State, Binding, Environment, and AppStorage
- Hydrate user-defined dynamic properties recursively: nested framework
  wrappers bind depth-first with stable declaration-ordered property
  indices, and update() runs once per hydration before body evaluation
- Cover conformances, nested-state persistence, and the update cycle
  with tests
- Add @dynamicMemberLookup with writable key-path child bindings
- Add init(projectedValue:), optional promotion and unwrapping
  initializers, and the AnyHashable projection
- Add conditional Identifiable, Sequence, Collection, Bidirectional-,
  and RandomAccessCollection conformances with writable element
  bindings
- Mark Binding @unchecked Sendable when its value is Sendable
- Cover writeback semantics for member, optional, and element bindings
- Add SwiftUI's init(initialValue:) spelling and the empty initializer
  for nil-expressible state values
- Drop the struct-level Codable constraint; persistence runs through
  family-specific read/write steps on the storage box
- Add SwiftUI's typed families (Bool, Int, Double, String, URL, Data,
  Date, RawRepresentable with String or Int raw values) plus their
  optional variants, where nil assignment removes the stored value
- Rename the backend parameter to SwiftUI's store label; it accepts a
  StorageBackend as the documented terminal adaptation of UserDefaults
- Keep arbitrary Codable values as a documented additive convenience
- Add SwiftUI's Bindable property wrapper with dynamic member lookup
  producing writable bindings into @observable models
- Provide the wrappedValue, unlabeled, and projectedValue initializer
  spellings plus conditional Identifiable and DynamicProperty
  conformances
- The synthesized internal init(wrappedValue:) violated the
  property-wrapper accessibility requirement; a private designated
  initializer now backs the public Observable-constrained spellings
- Add Bindable and custom dynamic property sections to the state
  management article and describe the typed AppStorage families
- Extend the data-flow compile fixture with wrapper families, dynamic
  member bindings, and a custom dynamic property
- Binding is now Sendable for Sendable values per the SwiftUI
  reference, so the positive fixture asserts it and the negative
  fixture rejects bindings to non-Sendable values instead
- The 'is' checks tripped the warning-fatal gate ("'is' test is always
  true"); generic requirement calls prove the conformances at compile
  time instead
…rface

- Drop the two stale overrides from the reshaped AppStorage wrapper
- Add overrides for the new surface: DynamicProperty, Bindable, the
  Binding projection initializers and conditional conformances, the
  State initializers, and the typed AppStorage families
- Regenerate the manifest with TUIkitAPICheck against fresh 6.0.3
  macOS and Linux snapshots
@phranck
phranck merged commit a87a43f into main Jul 22, 2026
6 checks passed
@phranck
phranck deleted the issue/18-data-flow-alignment branch July 22, 2026 12:18
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.

[P1-13] Align Binding, State, Environment, AppStorage, and Observation with SwiftUI

1 participant