Skip to content

BTraitsObserver: protocol-driven unified trait observation#4

Merged
kyleve merged 18 commits into
mainfrom
kve/traits-observation-updates
Apr 6, 2026
Merged

BTraitsObserver: protocol-driven unified trait observation#4
kyleve merged 18 commits into
mainfrom
kve/traits-observation-updates

Conversation

@kyleve

@kyleve kyleve commented Mar 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Unified trait observation: BTraitsValue declares an Observer associated type conforming to BTraitsValueObserver. BTraitsObserver coordinates per-type observers from BTraits registrations, using currentValue(from:) and makeObserver(with:onChange:), with idempotent start() / stop() at the coordinator.
  • Explicit observers: Every BTraitsValue implements makeObserver (no protocol default). NeverObserver remains available for traits with nothing live to subscribe to.
  • BTraits registration: .system, register(_:), and readCurrentValues(from:) centralize which types participate.
  • BContext: Separates baseTraits and subtree traitOverrides; merged traits drives equality and stylesheet config. BStylesheets keeps private trait/theme storage; BContext syncs via updateTraits / updateThemes (package-visible from the Swift package boundary).
  • BRootViewController: Lazy setup in viewIsAppearing; one BTraitsObserver(traits: .system, from: self) replaces ad hoc accessibility wiring.
  • BTraitOverridesViewController: Applies overrides from the full inherited context (parent traitCollection.bContext when embedded); refreshes in didMove(toParent:) and on trait changes; child creation still deferred to viewIsAppearing.
  • UIKit / SwiftUI: Trait bridging (BMode, BContentSizeCategory, UIViewControllerTraitObserver), SwiftUI environment and modifiers for BContext / overrides.
  • BAccessibility.Observer: Internal initializer only (no public observe factory); review chose to keep construction package-internal until a public API is needed.
  • Build layout: Root Package.swift ships BroadwayCore, BroadwayUI, and BroadwayTesting (sources under */Sources). Tuist generates the Xcode workspace for BroadwayCatalog, BroadwayTestHost, and .xctest bundles that depend on those products. BroadwayTesting in SPM links UIKit only (no .xctest link on the library); see Project.swift comment near the local package.
  • Concurrency: Sendable / @unchecked Sendable where required for Swift 6 (e.g. BMode, BContentSizeCategory, SettingsProvider usage).
  • Docs: AGENTS.md / README.md describe hybrid SPM + Tuist workflows and how to run tests.

Test plan

  • mise exec -- tuist test BroadwayCoreTests (trait observer, context, accessibility observer, stylesheets)
  • BTraitsObserverTests: show()-hosted lifecycle, double-start idempotency (CountingStartSpy), stop tears down notification subscription (SeqTrait + dedicated NotificationCenter)
  • BRootViewControllerTests / trait-override container tests for inherited context and traitOverrides.bContext
  • BAccessibilityObserverTests cover Observer lifecycle and injected NotificationCenter behavior

Made with Cursor

kyleve added 5 commits March 31, 2026 15:42
Show only directories instead of enumerating individual files.
Add missing BroadwayTestHost and BroadwayTesting to README.

Made-with: Cursor
Add BTraitsValueObserver protocol and associated type to BTraitsValue
so each trait declares its own observer. BTraitsObserver coordinates
them behind a single start/stop interface, simplifying BRootViewController.

Remove BAccessibility.observe(_:) factory in favor of direct Observer init.

Made-with: Cursor
BTraits now owns the set of registered trait types via register<V>()
and a .system factory. BTraitsObserver reads registrations and the
UIViewController hierarchy from the BTraits instance instead of
hardcoding types. initialValue replaced with currentValue(from:)
for future injectability. EquatableIgnored gains Hashable conformance.

Made-with: Cursor
@kyleve

kyleve commented Mar 31, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4b306ccf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread BroadwayCore/Sources/Traits/Values/BAccessibility.swift
Comment thread BroadwayCore/Sources/Traits/BTraitsObserver.swift
kyleve added 5 commits March 31, 2026 17:11
Defer child VC creation, trait observation, and context setup until
the controller enters a valid view hierarchy (viewIsAppearing).
Replace the XCTestCase extension with standalone free functions
compatible with Swift Testing.

Made-with: Cursor
…e BAccessibility trait conformance into the BAccessibility file.
…ewController

Move BTraits, BTraitsObserver, and BAccessibility into
BroadwayCore/Sources/Traits/. Extract trait value conformances
into Values/. Add BTraits+Overrides for override support and
BTraitOverridesViewController for propagating trait overrides
through the view hierarchy.

Made-with: Cursor
…gnored for caches

Flatten BStylesheets to store traits/themes directly instead of
through a Config struct. Mark cache and creating arrays with
@EquatableIgnored so equality is based solely on traits and themes.
Add @unchecked Sendable to EquatableIgnored and mark stylesheets
as @EquatableIgnored on BContext.

Made-with: Cursor
…troller fix

Pass UIViewController through to BTraitsValue.makeObserver so
VC-scoped traits can use registerForTraitChanges. Add
UIViewControllerTraitObserver as a reusable observer for UIKit
trait-backed values. Wire BMode and BContentSizeCategory to read
from UITraitCollection and observe changes. Fix
BTraitOverridesViewController with proper child containment and
lazy setup. Bridge BContext into SwiftUI via
UITraitBridgedEnvironmentKey and add bTraitOverrides/bMode/
bContentSizeCategory view modifiers.

Made-with: Cursor
@kyleve

kyleve commented Apr 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f0a2f072a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread BroadwayUI/Sources/BTraitOverridesViewController.swift Outdated
Comment thread BroadwayCore/Sources/BContext.swift Outdated
Comment thread BroadwayCore/Sources/Traits/Values/BAccessibility.swift
@kyleve

kyleve commented Apr 4, 2026

Copy link
Copy Markdown
Owner Author

cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7f0a2f0. Configure here.

Comment thread BroadwayUI/Sources/BTraitOverridesViewController.swift
Comment thread BroadwayCore/Sources/BContext.swift Outdated
kyleve added 2 commits April 4, 2026 22:22
- Merge main agent tooling (sync-agents, nested AGENTS.md, external skills)
- Resolve AGENTS.md and README.md conflicts with flat layout trees
- Keep README Targets table; document AI skills via sync-agents

Made-with: Cursor
- Seed BStylesheets with merged traits when BContext init takes overrides.
- Apply trait overrides from inherited parent bContext and republish full context.
- Prefer parent traitCollection when resolving inherited BContext during viewIsAppearing.
- Make BTraitsObserver.start/stop idempotent at the coordinator level.
- Add BContext and show()-based BroadwayUI tests for the above.

Made-with: Cursor
Comment thread BroadwayCore/Sources/Traits/BTraits.swift Outdated
Comment thread BroadwayCore/Tests/BContextTests.swift Outdated
Comment thread BroadwayCore/Tests/BContextTests.swift Outdated
Comment thread BroadwayCore/Tests/BTraitsObserverTests.swift Outdated
Comment thread BroadwayCore/Tests/BTraitsObserverTests.swift Outdated
Comment thread BroadwayUI/Sources/BTraitOverridesViewController.swift
Comment thread Project.swift
kyleve added 4 commits April 4, 2026 23:06
- Align BMode.currentValue with BContentSizeCategory via BMode.from(_: UIUserInterfaceStyle).
- Require explicit BTraitsValue.makeObserver (drop NeverObserver default extension); update ScaleFactor tests.
- Make BStylesheets traits storage private; sync via updateTraits from BContext only; stop reading traits from tests.
- BTraitsObserverTests: use show() with a retained anchor VC; assert coordinator double-start only calls underlying start once (CountingStartSpy).
- BTraitOverridesViewController: apply overrides from didMove(toParent:) when embedded.
- Restore BroadwayTesting dependency on .xctest (matches main).

Made-with: Cursor
- Make themes storage private; add updateThemes like updateTraits.
- Theme propagation test resolves theme via lazy TestStylesheet get().

Made-with: Cursor
- Mark updateTraits/updateThemes as package; document Tuist -package-name.
- Apply OTHER_SWIFT_FLAGS -package-name Broadway to all first-party targets
  so package symbols compile outside a SwiftPM Package.swift layout.

Made-with: Cursor
- Add Package.swift with BroadwayCore, BroadwayUI, BroadwayTesting (iOS 26 + Mac Catalyst); target paths use */Sources so Tests stay out of libraries.
- Tuist Project.swift: local package at repo root; Catalog/TestHost/CoreTests/UITests/CatalogTests depend on package products; drop generated framework targets and -package-name flags.
- Sendable: BMode/BContentSizeCategory; SettingsProvider + @unchecked Sendable system/mock providers for SPM concurrency checks.
- Update AGENTS.md and README for hybrid SPM + Tuist workflows and scheme names.

Made-with: Cursor
Comment thread BroadwayCore/Tests/BContextTests.swift Outdated
kyleve added 2 commits April 6, 2026 14:53
…gthen stop test

- Remove redundant stylesheet get in BContext merged-traits test (kyleve)
- Note why BroadwayTesting no longer links .xctest under Tuist+SPM
- BTraitOverridesViewController: document didMove vs viewIsAppearing roles
- NeverObserver doc: reflect explicit makeObserver requirement
- BTraitsObserverTests: SeqTrait + NotificationCenter proves stop removes subscriptions; fix CountingStartSpy static storage; isolate NC per hosted VC

Made-with: Cursor
@kyleve kyleve merged commit 3364376 into main Apr 6, 2026
2 checks passed
@kyleve kyleve deleted the kve/traits-observation-updates branch April 8, 2026 21:39
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