Repository navigation
feat(routing)!: report navigation through plain observables of values - #4497
Merged
Merged
Conversation
RoutingState now reports navigation through IObservable<T> streams of domain values instead of a change-set stream, and routing no longer depends on DynamicData in either the lean or the System.Reactive flavour. - NavigationStackChanged emits a read-only snapshot of the whole stack after each change, oldest view model first. - CurrentViewModel emits the view model on top of the stack on subscribe and after each change, and is typed as nullable because it emits null while the stack is empty. - CanNavigateBack emits whether the stack holds a view model to go back to, on subscribe and whenever the answer changes. NavigateBack uses it as its can-execute stream. - The focus mixins (WhenNavigatedTo, WhenNavigatedToObservable, WhenNavigatingFromObservable), the MAUI RoutedViewHost stack sync and the UIKit RoutedViewHost now follow NavigationStackChanged. - The ReactiveUI.Routing and ReactiveUI.Routing.Reactive projects, their tests, the change-set count benchmark and the DynamicData package version are removed. BREAKING CHANGE: routing exposes plain observables of values and the DynamicData routing packages are gone. - RoutingState.NavigationChanges (IObservable<IReactiveChangeSet<IRoutableViewModel>>) -> RoutingState.NavigationStackChanged (IObservable<IReadOnlyList<IRoutableViewModel>>). It emits after each change only; read RoutingState.NavigationStack for the stack as it is when you subscribe. - RoutingState.CurrentViewModel: IObservable<IRoutableViewModel> -> IObservable<IRoutableViewModel?>. The values are unchanged. - Counting stack changes to enable a back button -> RoutingState.CanNavigateBack (IObservable<bool>). - ReactiveUI.Routing / ReactiveUI.Routing.Reactive packages -> removed; drop the package reference. - RoutingStateDynamicDataMixins.NavigationChanged() -> RoutingState.NavigationStackChanged. - DynamicDataChangeSetMixins.CountChanged() / HasCountChanged() -> ChangeSetMixins.WhenCountChanged() / CountHasChanged() on IReactiveChangeSet<T> from ToReactiveChangeSet(). - DynamicDataInteropMixins.ToDynamicDataChangeSet() -> removed; call DynamicData's ToObservableChangeSet() on the collection in your own code. - DynamicDataAutoPersistMixins.ActOnEveryObject() on IChangeSet<T> -> AutoPersistHelperMixins.ActOnEveryObject() on the collection or on an IReactiveChangeSet<T> stream. Closes #4478
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4497 +/- ##
==========================================
+ Coverage 93.42% 93.63% +0.20%
==========================================
Files 168 163 -5
Lines 6346 6268 -78
Branches 733 728 -5
==========================================
- Hits 5929 5869 -60
+ Misses 416 398 -18
Partials 1 1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
3 of 4 tasks
glennawatson
added a commit
that referenced
this pull request
Sep 26, 2026
## Summary **Runnable documentation examples now cover the public API of every lean ReactiveUI package, one page project per handbook page.** - **New page projects under `src/examples/Documentation/Pages`.** Each project matches one page of the handbook on reactiveui.net (view models, commands, bindings, activation, interactions, view location, routing, collections, message bus, scheduling, persistence, testing, the app builder, registration, reflection) and runs as a console app that prints what each example does. - **Platform page projects for WPF, WinForms, WinUI, Blend and Drawing, MAUI, Blazor and Android.** Each is a small realistic app (a school timetable, a weather station dashboard, a course list) that runs its scenario end to end and prints the result; the Windows apps expose `--smoke`. - **Every example states its output.** Each example method ends in an `// Output:` block, and the website quotes the examples verbatim. - **Examples follow the merged fixes.** Routing uses the #4497 navigation streams, WinUI shows the #4496 AOT-safe hosts with their `*Unsafe` twins as the explicit opt-in, and the builder, MAUI host, ReactiveRecord, suspension and Android orientation examples show the fixed behaviour. - **CI tests only the projects under `tests/`.** The solution now builds the example apps, and `dotnet test` over the whole solution tried to run them, including the Android and MAUI apps, which need a device. - **The examples build with the same rules as Refit's.** `src/examples/Documentation` carries Refit's `.editorconfig` (explicit types instead of `var`), and `src/examples/Directory.Build.props` marks the examples as not packable. ## Why **The handbook needs an example for every public member, and each example has to compile and run against the current code.** - The pages on reactiveui.net quote these projects, so a page cannot drift from the API without a build or output check failing. - Members that no example calls are reviewed one by one: the platform or the framework calls them, another ReactiveUI package uses them, or they need hardware the examples cannot run on. ## Breaking changes **None.** The examples are not packaged. ## How this was verified **Every page project builds with no warnings, and each project's printed output matches its `// Output:` blocks.** The Windows apps ran their `--smoke` scenario on Windows, and the Android app ran its full scenario on an emulator. iOS and Mac Catalyst were not run. ## Notes for the reviewer **The code is sample code, so review it for readability and realism, not for architecture.** - Start with any one page project's `Program.cs`, which lists that page's examples in reading order. - `src/examples/Documentation/Directory.Build.props`, `src/examples/Directory.Build.props`, `.editorconfig` and the `reactiveui.slnx` entries are the only build changes. - Overload families such as `WithInstance<T1..T16>` are shown at one and two arguments only; the pages state how many the family supports. - The WinUI maintenance panel uses `RoutedViewHostUnsafe` and `ViewModelViewHostUnsafe` for a view added with `Map`, because the default hosts cannot find a mapped view until #4502 is fixed. ## Checklist - [x] I have read the [Contribute guide](https://www.reactiveui.net/contribute/index.html) - [x] The PR title follows [Conventional Commits](https://www.conventionalcommits.org/) - [x] Tests cover this change, or the summary says why they do not - [ ] New or changed public API has XML documentation
|
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
RoutingStatereports navigation through plainIObservable<T>streams of values, and routing no longer depends on DynamicData.NavigationStackChangedreplacesNavigationChanges. It emits a read-only snapshot of the whole navigation stack after each change, oldest view model first, instead of a change set.CanNavigateBackis new. It emits whether the stack holds a view model to go back to, when you subscribe and whenever the answer changes.NavigateBackuses it to decide when it can execute.CurrentViewModelis typedIObservable<IRoutableViewModel?>. It already emittednullwhile the stack was empty; the type now says so.WhenNavigatedTo,WhenNavigatedToObservable,WhenNavigatingFromObservable, the MAUIRoutedViewHoststack sync and the UIKitRoutedViewHostno longer use change sets orCollectionChangedevents.ReactiveUI.RoutingandReactiveUI.Routing.Reactivepackages are removed, with their tests, the change-set count benchmark and the DynamicData package version.Why
Routing exposed a generic change-set type that consumers had to decode, and the DynamicData interop lived in separate packages that pulled System.Reactive back in.
Closes #4478
Breaking changes
Routing members and packages changed; each has a direct replacement.
RoutingState.NavigationChangesis removed. Subscribe toRoutingState.NavigationStackChanged, which emits after each change only; readNavigationStackfor the stack as it is when you subscribe.RoutingState.CurrentViewModelis nowIObservable<IRoutableViewModel?>. Handle thenullit emits for an empty stack.RoutingState.CanNavigateBack.ReactiveUI.RoutingandReactiveUI.Routing.Reactivepackages are gone.NavigationChanged()becomesNavigationStackChanged;CountChanged()andHasCountChanged()becomeWhenCountChanged()andCountHasChanged()onToReactiveChangeSet();ActOnEveryObjecton a DynamicData change set becomesActOnEveryObjecton the collection;ToDynamicDataChangeSet()has no replacement, so call DynamicData'sToObservableChangeSet()in your own code.How this was verified
New tests cover every new and changed
RoutingStatemember, the focus mixins' seeding, error and completion paths, and the MAUI stack resync on clear, in both the lean and System.Reactive flavours; a lean console app subscribes to a signal with a lambda and to all three routing streams without loading System.Reactive or DynamicData.RoutedViewHostcompiles only for Apple targets and has no automated test here.Notes for the reviewer
Start with
RoutingStateandRoutableViewModelMixins; the project, package and benchmark removals are mechanical.CurrentViewModelandCanNavigateBackderive from theNavigationStackChangedproperty at subscribe time, so a subclass that replaces that stream drives all three.Checklist