From dotnet-skills
Deploying Uno Platform apps. Per-target guidance for WASM, iOS, Android, macOS, Windows, Linux.
npx claudepluginhub wshaddix/dotnet-skillsThis skill uses the workspace's default tool permissions.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Per-target deployment guidance for Uno Platform applications: Web/WASM, iOS, Android, macOS (Catalyst), Windows, Linux (Skia/GTK), and Embedded (Skia/Framebuffer). Each target section covers project setup, debugging workflow, packaging/distribution, platform-specific gotchas, AOT/trimming implications, and behavior differences from other targets.
Scope boundary: This skill owns per-target deployment, debugging, packaging, and platform-specific gotchas. Core Uno Platform development (Extensions, MVUX, Toolkit, themes) is owned by [skill:dotnet-uno-platform]. MCP integration for live docs is owned by [skill:dotnet-uno-mcp].
Out of scope: Uno Platform testing (Playwright for WASM, platform Appium tests) -- see [skill:dotnet-uno-testing]. General AOT/trimming patterns -- see [skill:dotnet-aot-wasm]. UI framework selection -- see [skill:dotnet-ui-chooser].
Cross-references: [skill:dotnet-uno-platform] for core development, [skill:dotnet-uno-mcp] for MCP integration, [skill:dotnet-uno-testing] for testing, [skill:dotnet-aot-wasm] for general WASM AOT patterns, [skill:dotnet-ui-chooser] for framework selection.
| Target | TFM | Tooling | Packaging | Key Constraints |
|---|---|---|---|---|
| Web/WASM | net8.0-browserwasm | Browser DevTools | Static hosting / Azure SWA | No filesystem access, AOT recommended, limited threading |
| iOS | net8.0-ios | Xcode / VS Code / Rider | App Store / TestFlight | Provisioning profiles, entitlements, no JIT |
| Android | net8.0-android | Android SDK / Emulator | Play Store / APK sideload | SDK version targeting, permissions |
| macOS (Catalyst) | net8.0-maccatalyst | Xcode | Mac App Store / notarization | Sandbox restrictions, entitlements |
| Windows | net8.0-windows10.0.19041 | Visual Studio | MSIX / Windows Store | WinAppSDK version alignment |
| Linux | net8.0-desktop | Skia/GTK host | AppImage / Flatpak / Snap | GTK dependencies, Skia rendering |
| Embedded | net8.0-desktop | Skia/Framebuffer | Direct deployment | No windowing system, headless rendering |
TFM note: Use version-agnostic globs (net*-ios, net*-android) when detecting platform targets programmatically to avoid false negatives on older or newer TFMs.
# Run the WASM target
dotnet run -f net8.0-browserwasm --project MyApp/MyApp.csproj
The WASM target renders XAML controls in the browser. The renderer depends on project configuration: Skia (canvas/WebGL) or native HTML mapping. The app loads via a JavaScript bootstrap (uno-bootstrap.js) that initializes the .NET WASM runtime.
dotnet run starts a development server with live reload supportILogger output appears in the browser console# Publish for production
dotnet publish -f net8.0-browserwasm -c Release --output ./publish
# Output is a static site (HTML/JS/WASM)
# Deploy to: Azure Static Web Apps, GitHub Pages, Netlify, any static host
Published output is a self-contained static site. No server-side runtime required.
Task.Run may not parallelize on WASMTrimming reduces download size by removing unused code. AOT pre-compiles IL to WebAssembly, improving runtime execution speed but increasing artifact size. Use both together and measure the tradeoffs for your app.
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-browserwasm'">
<WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
Trimming is critical for WASM. Untrimmed apps can exceed 30MB. With trimming, typical apps are 5-15MB. AOT adds to the artifact size but eliminates interpreter overhead at runtime -- profile both download time and execution speed in target conditions.
For Uno-specific AOT gotchas (linker descriptors, Uno source generators), see the AOT section. For general WASM AOT patterns, see [skill:dotnet-aot-wasm].
FileOpenPicker maps to <input type="file"># Build for iOS simulator
dotnet build -f net8.0-ios
# Run on simulator
dotnet run -f net8.0-ios
Requires Xcode installed on macOS. The renderer (Skia or native) depends on project configuration and Uno version.
DOTNET_MODIFIABLE_ASSEMBLIES=debug# Publish for App Store
dotnet publish -f net8.0-ios -c Release \
/p:CodesignKey="Apple Distribution: MyCompany" \
/p:CodesignProvision="MyApp Distribution Profile"
Distribution channels: App Store (requires Apple Developer account), TestFlight (beta testing), Ad Hoc (enterprise).
Required: Provisioning profiles, signing certificates, entitlements file for capabilities (push notifications, HealthKit, etc.).
BGTaskScheduler for background workNSAppTransportSecurity exception in Info.plistMemoryWarning eventsiOS requires AOT by default (no JIT). The .NET runtime compiles to native ARM64 code.
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Gotcha: Reflection-heavy code fails silently on iOS. Test with trimming enabled during development, not just for release builds.
ASWebAuthenticationSession for OAuth; biometric auth via LocalAuthentication framework# Build for Android
dotnet build -f net8.0-android
# Deploy to connected device or emulator
dotnet run -f net8.0-android
Requires Android SDK (installed via dotnet workload install android or Android Studio).
emulatoradb logcat for runtime logs, adb devices to verify connections# Publish signed APK/AAB (use env vars or CI secrets for passwords — never hardcode)
dotnet publish -f net8.0-android -c Release \
/p:AndroidKeyStore=true \
/p:AndroidSigningKeyStore=mykey.keystore \
/p:AndroidSigningStorePass="$ANDROID_KEYSTORE_PASS" \
/p:AndroidSigningKeyAlias=myalias \
/p:AndroidSigningKeyPass="$ANDROID_KEY_PASS"
Google Play requires Android App Bundle (AAB) format. Sideloading uses APK.
<SupportedOSPlatformVersion> controls minimum API level. Target API 34+ for Google Play compliance@Keep annotations or linker configuration for native interop types<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PublishTrimmed>true</PublishTrimmed>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
AOT on Android improves startup time. Unlike iOS, JIT is available as fallback for untrimmed code paths.
BackRequested event fires automaticallyCustomTabs (Chrome) for OAuth; biometric auth via AndroidX.Biometric# Build for macOS
dotnet build -f net8.0-maccatalyst
# Run on macOS
dotnet run -f net8.0-maccatalyst
Uses Mac Catalyst (iOS APIs adapted for macOS). Requires Xcode on macOS.
# Publish for distribution
dotnet publish -f net8.0-maccatalyst -c Release \
/p:CodesignKey="Developer ID Application: MyCompany" \
/p:CodesignProvision="MyApp Mac Profile"
Distribution channels: Mac App Store, direct download with notarization, enterprise deployment.
Notarization required: macOS Gatekeeper requires notarized apps for distribution outside the App Store. Use xcrun notarytool to submit.
SupportedOSPlatform attributesSame profile as iOS (AOT by default for Catalyst). Trimming recommended for distribution builds.
ASWebAuthenticationSession (shared with iOS); supports Touch ID via Secure Enclave# Build for Windows
dotnet build -f net8.0-windows10.0.19041
# Run on Windows
dotnet run -f net8.0-windows10.0.19041
The Windows target can use either the Skia renderer or native WinAppSDK/WinUI 3 rendering.
# Package as MSIX
dotnet publish -f net8.0-windows10.0.19041 -c Release \
/p:PackageOutputPath=./packages
Distribution: Microsoft Store (MSIX), sideloading (MSIX with certificate), ClickOnce, or direct EXE.
10.0.19041 = Windows 10 2004+Windows.Storage, Windows.Networking APIs are available only on Windows target. Use conditional compilation or Uno abstractions<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
Windows supports both JIT and AOT. AOT produces a single native EXE with faster startup.
# Build for Linux desktop
dotnet build -f net8.0-desktop
# Run on Linux
dotnet run -f net8.0-desktop
The Linux target uses the Skia renderer with a GTK host window. All XAML rendering is done by Skia -- GTK provides only the window and input handling.
dotnet run outputs logs to terminal# Publish self-contained for Linux
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-x64
# Package as AppImage, Flatpak, or Snap
Distribution: AppImage (portable, no install), Flatpak (sandboxed), Snap (Ubuntu Store), DEB/RPM packages.
libgtk-3-0, libskia*GDK_SCALE=2 environment variable<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-desktop'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
AOT on Linux produces a native binary. Self-contained deployment avoids requiring a system-wide .NET runtime.
The Embedded target uses the Skia renderer with a framebuffer backend, enabling headless or kiosk-style rendering without a windowing system.
# Build for embedded (same TFM as desktop)
dotnet build -f net8.0-desktop -r linux-arm64
# Run directly on embedded device
dotnet run -f net8.0-desktop
The embedded target shares the net8.0-desktop TFM with Linux desktop. Platform-specific configuration selects the framebuffer backend.
// Program.cs -- framebuffer host configuration
public static void Main(string[] args)
{
SkiaHostBuilder.Create()
.UseFrameBuffer() // Use framebuffer instead of GTK
.App(() => new App())
.Build()
.Run();
}
dotnet-trace for remote profiling# Publish self-contained for ARM64
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-arm64
# Deploy binary directly to device filesystem
scp -r ./publish/* pi@device:/opt/myapp/
Direct deployment to device filesystem. No app store or package manager.
/dev/fb0)/dev/input/event*). No mouse cursor by defaultfbsetAOT is strongly recommended for embedded due to limited resources and startup time requirements.
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
InvariantGlobalization reduces binary size by removing ICU data (~28MB). Only use if the app does not need locale-specific formatting.
| Target | Back Navigation | Deep Linking | Gesture Navigation |
|---|---|---|---|
| Web/WASM | Browser back button / Alt+Left | URL-based routing | None |
| iOS | Swipe from left edge | URL schemes / Universal Links | Full gesture support |
| Android | Hardware/software back button | Intent filters / App Links | System back gesture (Android 13+) |
| macOS | Cmd+[ / toolbar back | URL schemes | None |
| Windows | Alt+Left / title bar back | Protocol activation | None |
| Linux | Alt+Left / keyboard | None | None |
| Embedded | Hardware button only | None | Touch only |
| Target | OAuth Flow | Token Storage | Biometric |
|---|---|---|---|
| Web/WASM | Browser redirect/popup | Browser secure storage | WebAuthn (limited) |
| iOS | ASWebAuthenticationSession | Keychain | Touch ID / Face ID |
| Android | Chrome Custom Tabs | Android Keystore | BiometricPrompt |
| macOS | ASWebAuthenticationSession | Keychain | Touch ID |
| Windows | System browser / WAM | Credential Manager | Windows Hello |
| Linux | System browser | Secret Service API | None |
| Embedded | Device-code flow | File-based (encrypt) | None |
| Target | IDE Debugger | Hot Reload | Profiling |
|---|---|---|---|
| Web/WASM | VS / VS Code (CDP) | Yes | Browser DevTools |
| iOS | VS (Pair to Mac) / VS Code / Rider | Yes | Xcode Instruments |
| Android | VS / VS Code | Yes | Android Profiler |
| macOS | VS (Pair to Mac) / VS Code / Rider | Yes | Xcode Instruments |
| Windows | Visual Studio | Yes (+ Live Visual Tree) | VS Diagnostic Tools |
| Linux | VS Code / Rider | Yes | dotnet-counters / dotnet-trace |
| Embedded | VS Code Remote | Yes (SSH) | dotnet-trace (remote) |
net*-ios, net*-android) to handle both .NET 8 and future .NET versions.Reflection.Emit, Expression.Compile(), or dynamic assembly loading will fail at runtime.dotnet workload install ios android maccatalyst wasm-tools