BlazorNativeApp
Namespace: BlazorNative.Runtime
The app-facing registration surface — the consumer API the 8.3
template calls. Lives in Runtime (not Core) because the mount thunk's type
is Func<NativeRenderer,int> and its consumer is HostSession —
putting it in Core would force a Core→Renderer dependency that inverts the
layering for nothing. Core stays pure contracts.
public static class BlazorNativeApp
Inheritance Object → BlazorNativeApp
Attributes NullableContextAttribute, NullableAttribute
Fields
DefaultRoute
The route ("/") every routed app must register a page for —
the target of the no-argument default mount and the route-aware initial mount.
public static string DefaultRoute;
Methods
RegisterPages(params BlazorNativePage[])
Called ONCE, at app startup, before the first mount (in a
NativeAOT app: from a [ModuleInitializer] — see the file header).
Validates loudly: non-empty array; factory-built rows only; unique
names; unique routes; and if any routed row exists, a
BlazorNativeApp.DefaultRoute row is required (Android's no-extra default
mount and the route-aware initial mount both resolve through it).
A second call, or a call after the derived views froze, throws
InvalidOperationException.
public static void RegisterPages(params BlazorNativePage[] pages)
Parameters
params pages BlazorNativePage[]
ConfigureServices(Action<IServiceCollection>)
Registers app-authored services into the framework's DI
container. Captured ONCE, at app startup, alongside
BlazorNativeApp.RegisterPages(params BlazorNativePage[]) (in a NativeAOT app: from a
[ModuleInitializer] — see the file header), and consumed ONCE by
the host session's composition root: configure runs
on the same Microsoft.Extensions.DependencyInjection.IServiceCollection the framework registered
its own services into, AFTER those registrations and immediately BEFORE
the single provider is built — so an app [Inject] reaches app
services exactly as it reaches framework ones. Additive and last-wins;
there is exactly ONE provider.
RESERVED CONTRACTS — the one limit on "last-wins" (#210). This
summary used to say a re-registration of a framework contract was "a
conscious last-write". That was not true of every contract, and the
exception was the dangerous kind: the framework re-resolves some of its
own registrations by concrete type after this delegate runs, so replacing
one did not override behaviour — it broke composition.
BlazorNative.Core.INavigationManager is the live case:
re-registering it used to throw InvalidCastException inside the
host session and fail EVERY mount with rc 2, naming nothing. It now fails
fast with a message that names the cause.
The rule, stated so it can be relied on: last-wins holds for your
services and for framework services the framework only ever resolves
through the same abstraction you replaced. It does not hold for
contracts documented CONSUME-ONLY — BlazorNative.Core.INavigationManager
and BlazorNative.Core.IMobileBridge — which the framework
implements and consumes itself. Adding services is always safe; replacing
a consume-only contract is not, and is rejected rather than half-honoured.
Never calling it is the baseline — the field stays null and the host session skips the invocation.
public static void ConfigureServices(Action<IServiceCollection> configure)
Parameters
configure Action<IServiceCollection>