Skip to content

BTraitsObserver: protocol-driven unified trait observation#4

Open
kyleve wants to merge 9 commits intomainfrom
kve/traits-observation-updates
Open

BTraitsObserver: protocol-driven unified trait observation#4
kyleve wants to merge 9 commits intomainfrom
kve/traits-observation-updates

Conversation

@kyleve
Copy link
Copy Markdown
Owner

@kyleve kyleve commented Mar 31, 2026

Summary

  • Introduce BTraitsValueObserver protocol and Observer associated type on BTraitsValue, so each trait type declares its own observer. A NeverObserver default means non-observed traits need no extra work.
  • Add BTraitsObserver as a unified coordinator that reads registered trait types from BTraits, initializes values from the view controller hierarchy via currentValue(from:), and manages per-type observers behind a single start()/stop() lifecycle.
  • BTraits gains a registration system (.system factory, register<V>(), readCurrentValues(from:)) so trait type configuration is centralized and extensible.
  • BRootViewController is simplified to a single BTraitsObserver(traits: .system, from: self) call, removing direct BAccessibility.Observer management.
  • Remove BAccessibility.observe(on:with:_:) factory in favor of direct Observer init with defaults.
  • Simplify directory trees in AGENTS.md and README.md.

Test plan

  • All existing tests pass (BroadwayCore, BroadwayUI, BroadwayCatalog)
  • New BTraitsObserverTests cover initial value population and lifecycle safety
  • BAccessibilityObserverTests updated to use Observer init directly
  • BRootViewControllerTests pass unchanged (behavioral parity)

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
Copy link
Copy Markdown
Owner Author

kyleve commented Mar 31, 2026

@codex review

Copy link
Copy Markdown

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

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 on lines 192 to 195
init(
notificationCenter: NotificationCenter,
settingsProvider: SettingsProvider,
notificationCenter: NotificationCenter = .default,
settingsProvider: any SettingsProvider = BAccessibility.systemSettings,
onChange: @MainActor @escaping @Sendable (BAccessibility, BAccessibility) -> Void,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make BAccessibility.Observer init public

This change removes the public BAccessibility.observe(...) factory, but the replacement initializer remains internal (init without public), which means modules outside BroadwayCore no longer have any public way to construct an accessibility observer. That is a source-breaking regression for existing consumers that previously observed accessibility updates; please make the initializer public (or keep a public factory) to preserve external usage.

Useful? React with 👍 / 👎.

Comment on lines +64 to +66
public func start() {
for observer in observers {
observer.start()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make BTraitsObserver.start idempotent

BTraitsObserver documents start() as safe to call multiple times, but this implementation always forwards start() to every underlying observer with no guard at the coordinator level. Custom BTraitsValueObserver types are public and may not be idempotent, so repeated start() calls can create duplicate subscriptions/callbacks; add an internal started-state check so the documented behavior is guaranteed.

Useful? React with 👍 / 👎.

kyleve added 4 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
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