-
-
Notifications
You must be signed in to change notification settings - Fork 122
Description
Context
Feedback from reactiveui/ReactiveUI#4296 by @ChaseFlorell highlights several documentation gaps that create confusion around the v21+ builder pattern, custom DI container integration, and how to configure global settings. The builder pattern is staying — it's required for AOT — but we need to better document the flexibility that already exists.
Problems to Address
1. Builder return value confusion
Users think they must capture the BuildApp() return into a variable, then IDEs warn about unused variables. We need to show:
- You don't have to assign the return value —
RxAppBuilder.CreateReactiveUIBuilder().WithBlazor().BuildApp();is valid - When you should capture it — to access
MainThreadScheduler,TaskpoolScheduler, orWithInstance<T>()
2. Custom DI container + builder integration is undocumented
The existing custom-dependency-inversion.md page shows the old InitializeSplat()/InitializeReactiveUI() pattern. It doesn't show how to use RxAppBuilder with a custom container like Autofac, DryIoc, or Microsoft.Extensions.DependencyInjection. Key APIs that exist but aren't documented well:
resolver.CreateReactiveUIBuilder()— extension method onIMutableDependencyResolverthat creates a builder using your resolverbuilder.WithRegistration(resolver => { ... })— register into your own resolverbuilder.UsingSplatModule<T>()— use existing Splat adapter modulesbuilder.ForCustomPlatform(scheduler, resolver => { ... })— full custom setup
3. DefaultExceptionHandler docs are outdated
default-exception-handler.md references RxApp.DefaultExceptionHandler which no longer exists in v21+. It needs to show the builder path (WithExceptionHandler()) and the direct API (once reactiveui/ReactiveUI#4302 lands).
4. No "existing app / hybrid bootstrap" migration scenario
The migration guide covers fresh setups but doesn't address the most common real-world case: "I already have a working app with its own DI and bootstrap — how do I integrate RxAppBuilder?"
Specific File Changes
Update: docs/handbook/rxappbuilder.md
Add a section "Using the BuildApp() Return Value" after the existing "Example Usage" section:
## Using the BuildApp() Return Value
`BuildApp()` returns an `IReactiveUIInstance`. You can use it to access configured schedulers
and resolve registered services:
\`\`\`csharp
var app = RxAppBuilder.CreateReactiveUIBuilder()
.WithWpf()
.BuildApp();
// Access schedulers
var mainScheduler = app.MainThreadScheduler;
var taskPoolScheduler = app.TaskpoolScheduler;
// Resolve registered services post-build
app.WithInstance<IScreen>(screen =>
screen.Router.Navigate.Execute(new MainViewModel(screen)));
\`\`\`
If you don't need the return value, you don't have to assign it:
\`\`\`csharp
// Perfectly valid — no variable needed, no IDE warnings
RxAppBuilder.CreateReactiveUIBuilder()
.WithBlazor()
.RegisterViews(v => v.Map<MyViewModel, MyView>())
.BuildApp();
\`\`\`Also add a section "Using RxAppBuilder with a Custom DI Container":
## Using RxAppBuilder with a Custom DI Container
If you use a custom Splat resolver (Autofac, DryIoc, Microsoft DI, etc.),
you can create the builder from your resolver directly:
\`\`\`csharp
// Set up your custom Splat adapter first
var autofacResolver = containerBuilder.UseAutofacDependencyResolver();
// Create the ReactiveUI builder using YOUR resolver
var app = autofacResolver
.CreateReactiveUIBuilder() // extension method on IMutableDependencyResolver
.WithWpf()
.WithRegistration(resolver =>
{
resolver.RegisterLazySingleton<IScreen>(() => new MainViewModel());
})
.BuildApp();
\`\`\`
The `CreateReactiveUIBuilder()` extension method on `IMutableDependencyResolver`
(in `ReactiveUI.Builder.RxAppBuilder`) wires the builder to use your container
instead of the default Splat locator. All registrations made through
`WithRegistration()` flow into your container.Update: docs/handbook/dependency-inversion/custom-dependency-inversion.md
The current examples use the legacy InitializeSplat() / InitializeReactiveUI() calls. Add a prominent note at the top:
> **ReactiveUI v21.0.1+**: If you are using the RxAppBuilder (recommended),
> see [Using RxAppBuilder with a Custom DI Container](~/docs/handbook/rxappbuilder.md#using-rxappbuilder-with-a-custom-di-container)
> for the modern approach. The patterns below still work for users not using the builder.Also update the Autofac example to show the builder-integrated version alongside the legacy version.
Update: docs/handbook/default-exception-handler.md
The page references RxApp.DefaultExceptionHandler which no longer exists. Update to show:
## Configuring the Exception Handler
### Via RxAppBuilder (recommended)
\`\`\`csharp
RxAppBuilder.CreateReactiveUIBuilder()
.WithWpf()
.WithExceptionHandler(new MyCoolObservableExceptionHandler())
.BuildApp();
\`\`\`
### Direct configuration (without builder)
\`\`\`csharp
// Available for apps with existing bootstrap pipelines
RxState.SetDefaultExceptionHandler(new MyCoolObservableExceptionHandler());
\`\`\`
> **Note**: The direct method is available from ReactiveUI vX.X+ (see reactiveui/ReactiveUI#4302).Update: docs/upgrading/rxappbuilder-migration.md
Add a new section "Scenario: Existing App with Custom DI" under Common Migration Scenarios:
### Scenario: Existing App with Custom DI Container
If you already have a fully wired DI container (Autofac, Microsoft DI, etc.)
and an existing bootstrap pipeline:
\`\`\`csharp
// 1. Set up your container as usual
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<MyService>().As<IMyService>();
// 2. Create the Splat adapter
var autofacResolver = containerBuilder.UseAutofacDependencyResolver();
containerBuilder.RegisterInstance(autofacResolver);
// 3. Create the ReactiveUI builder FROM your resolver
autofacResolver
.CreateReactiveUIBuilder()
.WithBlazor()
.RegisterViews(v => v
.Map<LoginViewModel, LoginView>()
.Map<MainViewModel, MainView>()
)
.BuildApp();
// 4. Continue your normal bootstrap
var container = containerBuilder.Build();
autofacResolver.SetLifetimeScope(container);
\`\`\`
**Key point**: `resolver.CreateReactiveUIBuilder()` is an extension method on
`IMutableDependencyResolver`. It creates a builder that uses YOUR container —
all ReactiveUI service registrations flow into it. You don't need to call
`InitializeSplat()` or `InitializeReactiveUI()` when using the builder.TOC changes needed
No new TOC entries required — all changes are to existing pages. If we decide to add a dedicated docs/handbook/dependency-inversion/builder-integration.md page later, add to docs/handbook/dependency-inversion/toc.yml:
- name: Builder Integration with Custom DI
href: builder-integration.mdSummary of files to edit
| File | Change |
|---|---|
docs/handbook/rxappbuilder.md |
Add "Using the BuildApp() Return Value" and "Using RxAppBuilder with a Custom DI Container" sections |
docs/handbook/dependency-inversion/custom-dependency-inversion.md |
Add v21+ note at top, add builder-integrated Autofac example |
docs/handbook/default-exception-handler.md |
Update to show builder and direct API paths (remove stale RxApp.DefaultExceptionHandler reference) |
docs/upgrading/rxappbuilder-migration.md |
Add "Existing App with Custom DI Container" migration scenario |
Related: reactiveui/ReactiveUI#4296, reactiveui/ReactiveUI#4302, reactiveui/ReactiveUI#4303