Skip to content
Shiny.NET
Shiny MAUI Shell v7 - App Links, App Shortcuts, & Navigation Interception!Shortcut me to it

AppDeviceBridge Releases

Feature
Shiny.AppDeviceBridge. Ship a web app — Blazor WebAssembly, React, Vue, anything that builds to static files — inside a .NET MAUI app, served straight out of its zip by a loopback Shiny.Net.HttpServer, updated over the air from a signed release server, and calling native device features through bridges. Android, iOS, Mac Catalyst and Windows, plus the maui-labs macOS (AppKit) and Linux (GTK4) heads. Formerly developed as WebAppHost; the packages are now Shiny.AppDeviceBridge.* and the bridge packages drop their .Bridge segment (Shiny.AppDeviceBridge.Wifi).
Feature
Every bridge has a typed client, in C# and TypeScript. Each bridge ships a Shiny.AppDeviceBridge.{Bridge}.Client package holding its request and response contracts and a [BridgeClient] interface — ICalendarBridge, IWifiBridge, IPhotosBridge — implemented by a source generator, registered with Add{Name}BridgeClient(). The native bridge serializes the very same contracts, so the page and the device cannot disagree about a shape, and a page no longer builds routes or JSON objects by hand. Failures throw BridgeException with the status, the bridge’s error code and IsNotSupported; events are methods returning a subscription. The same clients are generated into TypeScript (@shinyorg/appdevicebridge, in clients/typescript) from the same assemblies, with a test that fails when the committed output falls behind. See Typed clients.
Feature
Your own bridges get typed clients too. Declare the API once with [BridgeGet], [BridgePost], [BridgePut], [BridgeDelete] and [BridgeEvent] on an interface in a plain net10.0 project; route tokens, bodies and query parameters bind by convention, and anything the generator cannot turn into a request is build error ADB001 rather than a runtime surprise.
Feature
Photos bridge — Shiny.AppDeviceBridge.Photos. AddPhotosBridge() adds the system photo picker, which needs no permission and works on every head, and browsing the photo library newest first with thumbnails and full-size exports — PhotoKit on Apple platforms, MediaStore on Android, the Pictures folder on Windows. Photos reach the page as files in a file root. The library answers 501 on Linux, which has none. See Photos.
Feature
Folders bridge — Shiny.AppDeviceBridge.Folders. AddFoldersBridge() shows the platform’s own folder picker and turns the chosen folder into a file root the page reads and writes through the files bridge, remembered across launches: a security-scoped bookmark on Apple platforms, a persisted Storage Access Framework grant on Android, the path on Windows and Linux (GTK’s file dialog). See Picked folders.
Feature
File roots can be added while the app runs. WebAppFileRoots holds every root, and a bridge adds or removes a WebAppFileStore there; WebAppFileRoot is the directory-on-disk store, and a store that is not a directory — an Android Storage Access Framework tree — implements the same operations. Bridges that need a path on disk (sharing, transfers, notification images, tray icons) resolve through WebAppFileRoots.TryResolve and refuse a root that has none.
Feature
Tray icon bridge and launch at login. AddTrayIconBridge() puts a system tray or menu bar icon — menus, badges, notifications, animation — under the web app’s control on Windows, macOS and Linux, with clicks and menu choices handed back to the page or background.js. Launch at login is part of the app bridge (/_bridge/app/startup), over Shiny.Extensions.MauiHosting’s IStartupService.
Feature
Serve beyond the device, deliberately. RemoteAccess.Enabled binds past loopback, and bridges stay on the device unless each is named with RemoteAccess.AllowBridge. Your own endpoints, mapped with AddWebAppEndpoints and secured with AddWebAppAuthentication and AddWebAppAuthorization, are authenticated by default and reachable remotely under their own policies. BasePath and BridgePrefix move the app and the bridges independently, and pages discover them from _host/config.
Feature
Typed native calls in Blazor. WebAppNativeCalls.HandleAsync takes the payload’s JsonTypeInfoGpsReading, PushPayload, TransferInfo, JobRun — and, for calls that answer, the result’s, so a page handles background work with the same contracts as everything else.
Fix
A Blazor page delivered each event once per visit. WebAppEvents told its script listeners apart by the DotNetObjectReference, but Blazor hands the script a new object for that reference on every interop call, so unsubscribing never found the listener it was meant to remove: visiting a page three times and firing one event ran its handler three times, and the listeners — with everything they captured — were never released. Listeners are now keyed by an id the client owns, so leaving a page removes exactly its own.
BREAKING
Untyped bridge calls are gone from the Blazor client. WebAppBridge.GetAsync(path), PostAsync(path, JsonNode), PutAsync, DeleteAsync and GetStringAsync returned JsonElement and took JsonObject bodies; use the bridge’s typed client. WebAppBridge remains for endpoints of your own, typed through your JsonTypeInfo. WebAppNativeCalls.HandleAsync no longer takes JsonElement handlers returning JsonNode.
BREAKING
Wire changes that came with the contracts. The OBD bridge’s raw requests moved from POST obd/command with raw to POST obd/raw, since the two answer different shapes. speech.ended reports Stopped, PageClosed or Error, and POST speech/speak with "wait": false answers 204 instead of 202. Settings scopes are also accepted as Local and Secure. Built-in contract types were renamed along the way — WebAppHostInfo is HostInfo, WebAppLink is AppLink, WebAppFileEntry is FileEntry, WebAppSettingsList is SettingsList — and WebAppFileRoots.TryResolve is an instance method on the registered service.
BREAKING
File paths refuse the same characters on every platform. A path segment containing < > " | ? *, a colon or a control character is refused everywhere, not only where the file system happens to reject it, so a path means the same thing on Linux as it does on Windows.