WebAppHarness.Core
1.0.14
dotnet add package WebAppHarness.Core --version 1.0.14
NuGet\Install-Package WebAppHarness.Core -Version 1.0.14
<PackageReference Include="WebAppHarness.Core" Version="1.0.14" />
<PackageVersion Include="WebAppHarness.Core" Version="1.0.14" />
<PackageReference Include="WebAppHarness.Core" />
paket add WebAppHarness.Core --version 1.0.14
#r "nuget: WebAppHarness.Core, 1.0.14"
#:package WebAppHarness.Core@1.0.14
#addin nuget:?package=WebAppHarness.Core&version=1.0.14
#tool nuget:?package=WebAppHarness.Core&version=1.0.14
WebApp Harness
A cross-platform WebView harness for hosting web applications as native apps. Built with .NET MAUI, targeting Windows (WebView2), Android, iOS, and Mac Catalyst.
Features
- Full-page WebView hosting with configurable app URL
- DevTools support on all platforms (F12 on Windows, Chrome DevTools on Android, Safari Web Inspector on iOS/Mac Catalyst)
- CDP remote debugging port for UI automation (Windows/Android)
postMessage-based native bridge (window.nativeBridge) with request/response and event semantics- Pluggable telemetry pipeline with console and file sinks
- JSON configuration (
harness-config.json)
Quickstart
dotnet restore
dotnet build harness/dotnet/WebAppHarness.Core/WebAppHarness.Core.csproj # core library (no MAUI needed)
dotnet test harness/tests/WebAppHarness.Core.Tests/WebAppHarness.Core.Tests.csproj # run core tests
dotnet build harness/dotnet/WebAppHarness.App/WebAppHarness.App.csproj -f net10.0-windows10.0.19041.0 # MAUI app (Windows)
Prerequisites
- .NET 10 SDK (LTS). The repo pins the
10.0band viaglobal.json. - MAUI workload for any native shell build:
dotnet workload install maui- Or run the bootstrap script:
./scripts/setup.sh(macOS/Linux) or./scripts/setup.ps1(Windows).
- Or run the bootstrap script:
- Node.js 22+ for the web layer.
Per-platform native prerequisites (only what you actually build/run):
| Target | Requires |
|---|---|
Windows (net10.0-windows10.0.19041.0) |
Windows host + Windows 10 SDK (19041+) |
Android (net10.0-android) |
Android SDK |
iOS (net10.0-ios) |
macOS + Xcode |
Mac Catalyst (net10.0-maccatalyst) |
macOS + Xcode |
The WebAppHarness.Core library targets plain net10.0 and needs no MAUI workload.
Build
Build everything (requires MAUI workload):
dotnet build WebAppHarness.sln
Build just the Core library (no MAUI needed):
dotnet build harness/dotnet/WebAppHarness.Core/WebAppHarness.Core.csproj
Build the MAUI app for Windows only:
dotnet build harness/dotnet/WebAppHarness.App/WebAppHarness.App.csproj -f net10.0-windows10.0.19041.0
Running on Apple platforms (Mac Catalyst and iOS)
Windows (WebView2) is Windows-only, so on a Mac the native shell runs through the Apple WebView targets, which share a single WKWebView handler. From a Mac with Xcode + the MAUI workload:
Mac Catalyst (runs as a desktop app):
./scripts/setup.sh # one-time: install maui workload
dotnet build samples/SampleHost.App/SampleHost.App.csproj -f net10.0-maccatalyst -t:Run
iOS Simulator:
xcodebuild -downloadPlatform iOS # one-time: install a simulator runtime (if none present)
xcrun simctl boot "iPhone 16 Pro" # boot a simulator (create one via `simctl create` if needed)
open -a Simulator
dotnet build samples/SampleHost.App/SampleHost.App.csproj \
-f net10.0-ios -p:RuntimeIdentifier=iossimulator-arm64 -p:HarnessTargetFrameworks=net10.0-ios
xcrun simctl install booted samples/SampleHost.App/bin/Debug/net10.0-ios/iossimulator-arm64/SampleHost.App.app
xcrun simctl launch booted com.webappharness.samples
The web assets are bundled into the app and served from local wwwroot — no dev
server needed. WKWebView has no WebView2-style host mapping and cannot host a
Vite build over file:// (WebKit blocks cross-origin ES modules on file://),
so the harness registers a custom harness:// URL scheme handler that serves
wwwroot under a real origin (harness://app.harness.local). Set appUrl to
https://app.harness.local/index.html exactly as on Windows; the shared Apple
handler rewrites it onto the harness:// scheme automatically.
Troubleshooting (local builds):
- Xcode version mismatch — the iOS/maccatalyst SDKs pin an exact Xcode version.
If your installed Xcode is newer, add
-p:ValidateXcodeVersion=falseto build against it (safe within the same SDK major version). - Packing / single-platform builds on a Mac — the App/host csprojs list every
platform TFM (incl. Windows/Android, which can't build on macOS). To pack or
build a single platform, pass
-p:HarnessTargetFrameworks=net10.0-maccatalyst(ornet10.0-ios).
Run Tests
Run all tests:
dotnet test
Run only Core tests (no MAUI needed):
dotnet test harness/tests/WebAppHarness.Core.Tests/WebAppHarness.Core.Tests.csproj
Sample App
Reference sample apps are available in samples/. See samples/README.md for details.
Build the samples' web layer (and optionally the .NET host) with:
./samples/build.ps1 # web only -> SampleHost.App/wwwroot/
./samples/build.ps1 -DotNet # also packs harness packages to ./local-feed
# and builds SampleHost.App against them
SampleHost.App is not part of WebAppHarness.sln: it consumes the harness as
NuGet packages (from ./local-feed), just like an external app would.
The sample demonstrates:
- System Info -- query harness version, platform, and runtime details
- Echo -- request/response round-trip through the bridge
- Key-Value Storage -- read, write, delete, and list keys via native storage
- Telemetry -- fire events and errors through the native pipeline
- Event Log -- observe native events (e.g.
harness.ready) as they arrive
Configuration
Edit harness-config.json (copied to the output directory alongside the app):
{
"appUrl": "https://your-app.example.com",
"devTools": true,
"cdpPort": 9222,
"window": { "width": 1280, "height": 800, "title": "My App" },
"telemetry": {
"sinks": ["console", "file"],
"fileSink": { "path": "logs/", "maxSizeMb": 50 }
}
}
Bridge API (JavaScript)
The harness injects window.nativeBridge into every page:
// Request/response (returns Promise)
const result = await nativeBridge.request('echo', { message: 'hello' });
// Fire-and-forget
nativeBridge.invoke('telemetry.trackEvent', { name: 'click', props: {} });
// Listen for native events
const unsubscribe = nativeBridge.on('harness.ready', (data) => { ... });
// Built-in telemetry
nativeBridge.telemetry.trackEvent('page.loaded', { route: '/home' });
nativeBridge.telemetry.trackError(new Error('something broke'));
Built-in Capabilities
| Prefix | Method | Description |
|---|---|---|
echo |
(direct) | Returns whatever args you send (for testing) |
system |
getInfo |
Harness version, .NET runtime, OS, architecture |
system |
getPlatform |
Current platform (windows/android/ios/maccatalyst) |
storage |
get, set, delete, keys |
In-memory key-value storage |
telemetry |
trackEvent, trackError |
Route telemetry through the native pipeline |
Virtual Host Mapping
Place your web app files in wwwroot/ and set appUrl to
https://app.harness.local/index.html. Each platform serves that folder under a
stable origin so relative asset paths and ES modules resolve correctly:
- Windows (WebView2): maps
https://app.harness.local/towwwroot/viaSetVirtualHostNameToFolderMapping. - Apple — iOS and Mac Catalyst (WKWebView): WKWebView has no host-mapping API
and cannot host a Vite build over
file://, so a single shared Apple handler registers a customharness://URL scheme handler that serveswwwrootunderharness://app.harness.local/. The configuredappUrlis rewritten onto theharness://scheme automatically. The scheme handler returns proper HTTP 200 responses sofetch/response.ok-based loaders (e.g. PixiJS textures) work.
Project Structure
harness/
dotnet/
WebAppHarness.Core/ .NET class library (no MAUI dependency)
Bridge/ postMessage protocol, router, capabilities
Config/ JSON config model and loader
Telemetry/ Event pipeline, sinks (console, file)
Logging/ Structured logging interface
WebAppHarness.App/ .NET MAUI app shell
Platforms/ Platform-specific WebView handlers (Windows, Android)
WebView/ Shared Apple (iOS + Mac Catalyst) WebView handler
Resources/Raw/ Injected JS bridge shim
wwwroot/ Build output (bundled web app)
web/ Vite build system + shared JS utilities
tests/
WebAppHarness.Core.Tests/ xUnit tests for Core library
samples/ Reference sample apps
Consuming as a Dependency
The harness publishes three artifacts. Your web app is the "base app": you build
it into wwwroot/ using the harness toolchain, and the MAUI shell hosts it.
NuGet Packages (.NET shell)
WebAppHarness.Core— bridge/telemetry/config library (net10.0).WebAppHarness.App— the MAUI hosting shell, packaged as a library. Reference it, then provide a thin app head: deriveApp : HarnessAppand call.UseMauiApp<App>().UseWebAppHarness()in yourMauiProgram.
dotnet add package WebAppHarness.Core
dotnet add package WebAppHarness.App
npm Package (web toolchain + shared utilities)
npm install webappharness
Then drive your Vite build from the factory and use the shared utilities:
// vite.config.ts
import { createViteConfig } from 'webappharness/vite'
export default createViteConfig({ appDir: __dirname })
import { useBridge } from '@shared/BridgeContext' // alias injected by the factory
Local dogfooding (how the sample host consumes it)
samples/SampleHost.App consumes the harness exactly like an external app, but
from local build artifacts instead of the public registries:
.NET:build.ps1 -DotNetpacksWebAppHarness.Core/.Appinto./local-feed(registered innuget.config) and builds the host against those packages.- web:
samples/apps/bridge-demodeclares afile:dependency onwebappharnessand imports the factory by package name.
This means CI and the sample build exercise the exact published surface.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on WebAppHarness.Core:
| Package | Downloads |
|---|---|
|
WebAppHarness.App
MAUI hosting shell for WebAppHarness -- WebView2-based app harness with bridge, telemetry, and config infrastructure. |
GitHub repositories
This package is not used by any popular GitHub repositories.