This project is a Proof of Concept (PoC) demonstrating a "Host-Decoupling" architecture. It separates the native host (Android/iOS) from the UI logic by using Blazor WebAssembly within a Capacitor shell. This approach reduces dependency friction often found in complex hybrid frameworks like MAUI.
- Blazor WASM Integration: Core UI and business logic run on standard WebAssembly.
- Native Bridge: High-performance hardware access (Battery, Device Info, UUID) via JavaScript Interop to Capacitor plugins.
- Intelligent Web Fallback: Full debugging support in Windows/Browser environments by accessing real Browser APIs (Battery API, UserAgent, Navigator) when native Capacitor plugins are unavailable.
When you make changes to the C# code or Razor components, follow these steps to deploy the update to your physical device.
Instead of a simple build, you must perform a full Publish to ensure all WASM assets are optimized and the cap.js is correctly placed.
- In Visual Studio: Right-click the Project → Publish.
- Target: Folder Profile.
- Note: Ensure your
capacitor.config.jsonpoints to the publish directory:
"webDir": "bin/Release/net9.0/browser-wasm/publish/wwwroot"
Important
Use the Visual Studio UI for publishing if you have specific folder profile configurations. Manual CLI commands might miss critical asset bundling steps.
Open a terminal (PowerShell) in the project root and run:
npx cap sync androidThis command copies the fresh wwwroot files from your Blazor publish folder into the native Android project.
- Android: Open the project in Android Studio. Click the Green Play Button to install the new version on your connected device.
- iOS: Synchronize your changes (e.g., via GitHub), open the workspace in Xcode, and run it on your iPhone/iPad.
As discussed dotnet/aspnetcore#64871 in the official ASP.NET Core community, this setup offers:
- Resilience: Breaking changes in native SDKs (Xcode/Android) won't break your entire C# build environment.
- Performance: Blazor WASM runs at near-native speed within the high-performance WebView.
- Maintainability: Clear separation between Web-Standard UI and Native-Plugin hardware access.
Moving from a tightly coupled framework like MAUI to a decoupled Blazor-Capacitor architecture involves specific trade-offs. Understanding these is key to a successful implementation.
- Challenge: In MAUI, native logic is written in C#. With Capacitor, any native functionality must be bridged via JavaScript. This requires a context switch between languages during development.
- Benefit: You gain a Clean Interface. Capacitor enforces a "Parameters In / Parameters Out" pattern. This prevents "spaghetti code" where native logic is deeply tangled with UI logic. If the JS contract is solid, the app is stable.
- Challenge: You maintain a Double-Sided Model. Every new hardware feature requires an update in both the
cap.jsbridge and the C#DeviceResultclass. - Benefit: This forced discipline leads to a Platform-Agnostic Core. Your Blazor logic doesn't care if it runs on Android, iOS, or a Windows Browser; it simply talks to an interface.
- MAUI/WPF Approach: Often results in "Project Hell"—multiple platform-specific projects (Android, iOS, WinUI/WPF) with complex
#if ANDROIDor#if WINDOWSdirectives. - Capacitor Approach: You maintain a single Blazor WASM project. Whether you target Mobile (Capacitor), Web (Standard Browser), or Desktop (WebView2/Electron), the C# codebase remains 100% identical. The hardware abstraction is shifted to the thin JS layer.
- Debugging: You now operate two debuggers. C# logic is handled in Visual Studio, while Bridge/Native issues are inspected via Chrome DevTools (Remote Debugging).
- Lifecycle: Unlike MAUI’s native events, app lifecycle changes (Pause/Resume) must be explicitly "fired" from JavaScript to Blazor to ensure tasks like your "Polling-Bridge" logic react correctly when the app is backgrounded.
My main pain point remains the necessity of JavaScript; however, with the rising productivity of AI tools, this weight is becoming increasingly negligible.
When compared to the MAUI alternative—where you live with the daily uncertainty that a new framework bug might block a critical customer update or app release—this architecture feels like a 'blessed world.' It restores a level of reliability and predictability that we used to take for granted in the days of Winforms and WPF. We trade the convenience of a single language for the peace of mind that comes with stable, web-standard-based deployments.