Skip to main content

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 ObjectBlazorNativeApp
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>