Skip to content

refactor!: drop duplicate sinks for Primitives 5.7.0 concretes#4387

Merged
ChrisPulman merged 4 commits into
mainfrom
refactor/primitives-57-concrete-sinks
Jun 26, 2026
Merged

refactor!: drop duplicate sinks for Primitives 5.7.0 concretes#4387
ChrisPulman merged 4 commits into
mainfrom
refactor/primitives-57-concrete-sinks

Conversation

@glennawatson

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Refactor + dependency update. Bumps ReactiveUI.Primitives to 5.7.0 and removes in-repo observable sinks that the package now ships as concrete signal classes.

What is the new behavior?

  • ReactiveUI.Primitives updated 5.6.0 → 5.7.0.
  • The duplicated in-repo sinks are gone; call sites construct the equivalent Primitives concrete classes directly:
    • SingleValueObservable<T> / .Void / .True / .FalseReturnSignal<T>(value, Sequencer.Immediate) / ImmutableReturnRxVoidSignal.Instance
    • SyncExecuteObservable<T>StartSignal<T>(fn, Sequencer.Immediate)
    • StartObservable<T>StartSignal<T>(fn, scheduler)
  • Only the seam-neutral Primitives concretes (ReturnSignal, StartSignal, ImmutableReturnRxVoidSignal) are used, so the source still compiles under both the lean and .Reactive (System.Reactive interop) seams. ReactiveShim.props and the test Directory.Build.props import ReactiveUI.Primitives.Advanced (lean) / ReactiveUI.Primitives.Reactive.Advanced (shim) for unqualified resolution.
  • ReactiveUI.Core PublicAPI baselines updated to drop the removed public types.

What is the current behavior?

ReactiveUI carried its own ReactiveUI.Internal.SingleValueObservable<T>, SyncExecuteObservable<T> and StartObservable<T> sinks that duplicated functionality now provided by ReactiveUI.Primitives.

What might this PR break?

Breaking: the public types ReactiveUI.Internal.SingleValueObservable<T> and ReactiveUI.Internal.SyncExecuteObservable<T> are removed. They lived in the ReactiveUI.Internal namespace and were intended for internal use; consumers depending on them should construct the equivalent ReactiveUI.Primitives.Advanced signal classes (ReturnSignal, StartSignal). No runtime behavior change.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

Verified with -warnaserror:

  • Builds: ReactiveUI.Core, lean ReactiveUI (net8.0 + net10.0-android), ReactiveUI.Reactive (net8.0 + net10.0-android), and all four Apple TFMs (net10.0-ios/tvos/macos/maccatalyst) for both the lean and .Reactive leaves.
  • Tests: ReactiveUI.Tests net8.0 — 1966 passed, 0 failed; the .Reactive test leaf compiles.

Bump ReactiveUI.Primitives to 5.7.0 and delete in-repo observable sinks
that the package now provides as concrete signal classes.

- Remove ReactiveUI.Internal.SingleValueObservable<T> (+ Void/True/False
  singletons), SyncExecuteObservable<T>, StartObservable<T>
- Use the seam-neutral Primitives concretes instead: ReturnSignal,
  StartSignal and ImmutableReturnRxVoidSignal
- Import ReactiveUI.Primitives.Advanced (lean) /
  ReactiveUI.Primitives.Reactive.Advanced (shim) via ReactiveShim.props and
  the test Directory.Build.props so the names resolve under both seams
- Trim the removed public types from the ReactiveUI.Core PublicAPI baselines
- Drop the obsolete SingleValueObservable unit test

BREAKING CHANGE: the public types ReactiveUI.Internal.SingleValueObservable<T>
and ReactiveUI.Internal.SyncExecuteObservable<T> are removed; construct the
equivalent ReactiveUI.Primitives.Advanced signal classes instead.
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.96970% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.05%. Comparing base (6dc538e) to head (66b9739).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ctiveUI.Shared/Suspension/DummySuspensionDriver.cs 60.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4387      +/-   ##
==========================================
- Coverage   93.07%   93.05%   -0.02%     
==========================================
  Files         343      339       -4     
  Lines       14924    14882      -42     
  Branches     1570     1567       -3     
==========================================
- Hits        13890    13848      -42     
  Misses        770      770              
  Partials      264      264              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The near-identical EventHandler / EventHandler<TEventArgs> command-binding
overloads in the android and WinForms binders were copy-pasted, which
SonarCloud flagged as new duplication on the Primitives 5.7.0 PR. Hoist the
shared logic into one private static helper per binder instead.

- FlexibleCommandBinder: new BindEvent centralizes the command-parameter
  subscription, handler attach/detach and enabled-property sync. ForEvent
  (both overloads) and BindCommandToObject now only supply the typed handler
  wiring via a small closure.
- CreatesWinformsCommandBinding: new BindToHandler does the same for its two
  overloads, including the Component Enabled-property sync.
- Net ~57 fewer lines of platform code with identical behaviour (WinForms
  command-binding tests stay green).
The SonarCloud scan runs on windows-latest, so it executes the core, WPF,
WinUI and WinForms tests — that platform code stays measured (the WinForms
command binder reads ~99%). It cannot run the android/apple per-OS code under
ReactiveUI/Platforms/{android,apple-common,ios,mac,tvos,uikit-common,
mobile-common}, which needs an emulator, device or mac runner, so those files
always report 0% coverage and drag the new-code coverage gate down.

Exclude exactly those per-OS directories from coverage only. Platforms/net and
Platforms/netstandard2.0 stay measured (the core tests exercise them), and
every excluded file is still fully analysed for bugs, smells and duplication —
this is not a duplication exclusion.
The constructor kicked off the initial navigation-stack reconciliation with a
fire-and-forget Task.Run, mutating MAUI navigation state from a thread-pool
thread. That raced with any push happening right after construction: the
background SyncNavigationStacksAsync could interleave its own PushAsync with the
caller's, leaving CurrentPage pointing at the wrong page. It surfaced as the
intermittent RoutedViewHostTest.InvalidateCurrentViewModel_MatchingViewModelType_
UpdatesViewModel failure on the slower macOS CI runner.

Marshal the initial sync onto RxSchedulers.MainThreadScheduler instead — the
same pattern the rest of the class already uses (OnNavigateRequested). UI
navigation is now only ever mutated on the UI thread and the initial sync is
serialized with later navigation rather than racing it.

Verified: full ReactiveUI.Maui.Tests suite 202/202; RoutedViewHostTest stressed
20x with zero failures.
@sonarqubecloud

Copy link
Copy Markdown

@ChrisPulman ChrisPulman merged commit 6433b00 into main Jun 26, 2026
13 checks passed
@ChrisPulman ChrisPulman deleted the refactor/primitives-57-concrete-sinks branch June 26, 2026 12:16
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