Platform.Maui.Essentials.TvOS
0.1.0-alpha-0001
dotnet add package Platform.Maui.Essentials.TvOS --version 0.1.0-alpha-0001
NuGet\Install-Package Platform.Maui.Essentials.TvOS -Version 0.1.0-alpha-0001
<PackageReference Include="Platform.Maui.Essentials.TvOS" Version="0.1.0-alpha-0001" />
<PackageVersion Include="Platform.Maui.Essentials.TvOS" Version="0.1.0-alpha-0001" />
<PackageReference Include="Platform.Maui.Essentials.TvOS" />
paket add Platform.Maui.Essentials.TvOS --version 0.1.0-alpha-0001
#r "nuget: Platform.Maui.Essentials.TvOS, 0.1.0-alpha-0001"
#:package Platform.Maui.Essentials.TvOS@0.1.0-alpha-0001
#addin nuget:?package=Platform.Maui.Essentials.TvOS&version=0.1.0-alpha-0001&prerelease
#tool nuget:?package=Platform.Maui.Essentials.TvOS&version=0.1.0-alpha-0001&prerelease
.NET MAUI Backends for Apple TV & macOS (AppKit)
Custom .NET MAUI backends targeting platforms not officially supported by MAUI — Apple TV (tvOS via UIKit) and macOS (native AppKit, not Mac Catalyst).
Both backends use the platform-agnostic MAUI NuGet packages (net10.0 fallback assemblies) and provide custom handler implementations that bridge MAUI's layout/rendering system to the native platform UI frameworks.
Samples
Videos are attached in the repo
Project Structure
src/
Microsoft.Maui.Platform.TvOS/ # tvOS backend library (net10.0-tvos)
Microsoft.Maui.Platform.MacOS/ # macOS AppKit backend library (net10.0-macos)
Microsoft.Maui.Essentials.TvOS/ # tvOS Essentials library
Microsoft.Maui.Essentials.MacOS/ # macOS Essentials library
samples/
Sample/ # Shared sample code (App.cs, MainPage.cs, Platforms/)
SampleTv/ # tvOS sample app (links files from Sample/)
SampleMac/ # macOS sample app (links files from Sample/)
Note: There is also a
Sample/Sample.csprojthat multitargets bothnet10.0-tvosandnet10.0-macos, but it is not yet working. UseSampleTvandSampleMacto build and run the individual platform samples.
Handlers Implemented
Both platforms share the same set of control handlers:
| Control | tvOS (UIKit) | macOS (AppKit) |
|---|---|---|
| Label | UILabel | NSTextField (non-editable) |
| Button | UIButton | NSButton |
| Entry | UITextField | NSTextField (editable) |
| Editor | ❌ Not implemented | NSTextView (multiline, in NSScrollView) |
| Picker | UIButton + UIAlertController | NSPopUpButton |
| Slider | Custom TvOSSliderView | NSSlider |
| Stepper | ❌ Not implemented | NSStepper |
| Switch | UIButton (toggle, no native UISwitch on tvOS) | NSSwitch |
| CheckBox | ❌ Not available on tvOS | NSButton (checkbox style) |
| RadioButton | ❌ Not available on tvOS | NSButton (radio style) |
| SearchBar | Custom TvOSSearchBarView | NSSearchField |
| ActivityIndicator | UIActivityIndicatorView | NSProgressIndicator |
| ProgressBar | UIProgressView | NSProgressIndicator (bar mode) |
| Image | UIImageView | NSImageView |
| ScrollView | UIScrollView | NSScrollView |
| ShapeView | UIView + CAShapeLayer | NSView + CAShapeLayer |
| Border | UIView + CAShapeLayer (stroke + mask) | NSView + CAShapeLayer (stroke + mask) |
| DatePicker | ❌ No UIDatePicker on tvOS | NSDatePicker (date mode) |
| TimePicker | ❌ No UIDatePicker on tvOS | NSDatePicker (time mode) |
| Shadow | CALayer shadow properties | CALayer shadow properties |
| Layout (Stack, Grid, etc.) | TvOSContainerView | MacOSContainerView |
| CollectionView | UIScrollView (item materialization) | NSScrollView (item materialization) |
| CarouselView | UIScrollView (horizontal paging, snap-to-item) | ❌ Not implemented |
| ContentPage | TvOSContainerView | MacOSContainerView |
| ContentView | TvOSContainerView | MacOSContainerView |
| FlyoutPage | ❌ Not available on tvOS | FlyoutContainerView (NSSplitView sidebar) |
| Toolbar | ❌ Not available on tvOS | NSToolbar (via Page.ToolbarItems) |
| BoxView | via ShapeView | via ShapeView |
| GraphicsView | ❌ Not implemented | MacOSGraphicsView (DirectRenderer + CoreGraphics) |
| NavigationPage | NavigationContainerView (stack navigation) | NavigationContainerView (stack navigation) |
| TabbedPage | TabbedContainerView (custom tab bar) | TabbedContainerView (NSSegmentedControl) |
| WebView | ❌ Not available on tvOS | WKWebView |
| MapView | MKMapView (display-only) | MKMapView (interactive) |
| BlazorWebView | ❌ Not available on tvOS | MacOSBlazorWebView + WKWebView |
Infrastructure
| Component | tvOS | macOS |
|---|---|---|
| Application | NSObject | NSObject |
| Window | UIWindow + UIViewController | NSWindow + FlippedNSView |
| Dispatcher | GCD (DispatchQueue.MainQueue) | GCD (DispatchQueue.MainQueue) |
| DispatcherTimer | NSTimer | NSTimer |
| Dialogs (Alert, Confirm, Prompt) | ❌ Not supported (see below) | NSAlert |
Handler TODO
Controls
- ImageButton
- Stepper — tvOS ❌ (macOS ✅)
- RadioButton — tvOS ❌ (macOS ✅)
- Dialogs (Confirm, Prompt, Alert) - macOS ✅, tvOS ❌ (see Dialogs below)
Pages
- IndicatorView
Collections
- RefreshView
- SwipeView
Platform TODO
General
- Multitarget
Sampleproject (net10.0-tvos;net10.0-macosin a single csproj) - XAML Compilation (currently C#-only pages work reliably)
- Multi-window support
- Modal page presentation
- Keyboard/focus management
- Accessibility support
- Light/dark mode support (theme detection and dynamic colors) ✅
- Font management (custom fonts, font families)
- Gesture recognizers (Tap, Swipe, Pan, Pinch)
tvOS Specific
- Focus Engine integration (visual focus states on controls)
- Top Shelf extensions
- TV remote menu button handling
- TVUIKit integration (TVPosterView, TVMonogramView)
macOS Specific
- Menu bar integration (NSMenu)
- Touch Bar support
- NSSecureTextField for Entry.IsPassword (currently no-op)
- File dialogs (Open/Save panels)
- Drag and drop
- Multiple windows
Window lifecycle (minimize, fullscreen, close)✅ (active, resign active, hide, unhide, terminate)
Broader Goals
WebView— macOS ✅ (WKWebView), tvOS ❌ (not supported by platform)BlazorWebView— macOS ✅ (custom MacOSBlazorWebView control), tvOS ❌ (no WebView support)- App Icons (ideally via MAUI build tools /
MauiIcon) Essentials (platform-specific API wrappers)— AppInfo ✅, DeviceInfo ✅, Connectivity ✅, Battery ✅ (macOS only), DeviceDisplay ✅, FileSystem ✅, Preferences ✅, SecureStorage ✅, FilePicker ✅ (macOS only), MediaPicker ✅ (macOS only), TextToSpeech ✅, Clipboard ✅, Browser ✅ (macOS only), Share ✅ (macOS only), Launcher ✅ (macOS only) (see Essentials below)- NuGet packaging
- CI/CD pipeline
Prerequisites
Important: JetBrains Rider and Visual Studio will not compile these projects. The
net10.0-tvosandnet10.0-macosTFMs are not recognized by IDE build systems. All building and running must be done through the CLI using thedotnetcommand.
.NET 10 SDK
Install the latest .NET 10 preview SDK from dotnet.microsoft.com.
Workloads (macOS only)
The tvOS and macOS workloads must be installed. These are only available on macOS (requires Xcode).
# Install both workloads
dotnet workload install tvos
dotnet workload install macos
# Verify they are installed
dotnet workload list
You should see output similar to:
Installed Workload Id Manifest Version Installation Source
-----------------------------------------------------------------
macos 26.2.10197/10.0.100 SDK 10.0.100
tvos 26.2.10197/10.0.100 SDK 10.0.100
Xcode
Xcode must be installed (for Apple platform SDKs and the tvOS simulator). Ensure the command-line tools are selected:
sudo xcode-select -s /Applications/Xcode.app
Building
All builds must be done via the CLI:
# Build the backend libraries
dotnet build src/Microsoft.Maui.Platform.TvOS/Microsoft.Maui.Platform.TvOS.csproj
dotnet build src/Microsoft.Maui.Platform.MacOS/Microsoft.Maui.Platform.MacOS.csproj
# Build the sample apps (use SampleTv / SampleMac, not Sample)
dotnet build samples/SampleTv/SampleTv.csproj
dotnet build samples/SampleMac/SampleMac.csproj
Note: Do not use
dotnet build "MAUI Platforms.slnx"— the solution-level build may fail due to multitarget issues. Build projects individually instead. TheSamplemultitarget project is also not yet working — useSampleTvandSampleMacinstead.
Running
tvOS (Simulator)
dotnet build samples/SampleTv/SampleTv.csproj -t:Run
This builds, deploys to the tvOS simulator, and launches the app in one step.
macOS
dotnet build samples/SampleMac/SampleMac.csproj
open samples/SampleMac/bin/Debug/net10.0-macos/osx-arm64/MAUI\ macOS.app
Key Technical Notes
- MAUI NuGet packages resolve to the
net10.0(platform-agnostic) assembly for unsupported TFMs. This meansToPlatform()returnsobject— customViewExtensionscasts to the native view type (UIView/NSView). - The platform-agnostic
ViewHandlerhas no-opPlatformArrangeand returnsSize.ZerofromGetDesiredSize. Custom base handlers (TvOSViewHandler/MacOSViewHandler) override these to bridge MAUI layout to native view frames. - macOS NSView uses bottom-left origin by default. All container views override
IsFlipped => truefor MAUI's top-left coordinate system. - macOS NSView has no
SizeThatFits()— the base handler usesIntrinsicContentSizeandFittingSizefor native controls, and a customSizeThatFitsmethod onMacOSContainerView. IButtondoes not haveTextorTextColordirectly — those are onITextandITextStyle. Handlers cast viaif (button is IText textButton).- Sample apps use pure C# pages (no XAML) to avoid XAML compilation issues on unsupported platforms.
NavigationPagedispatches navigation requests viaHandler.Invoke()(the MAUI command mapper pattern), not direct method calls. The handler must register aCommandMapperentry forRequestNavigationand callNavigationFinished()on the view after completing navigation. The initial page is pushed automatically by MAUI'sOnHandlerChangedCore.
Lifecycle Events
Both platforms fire IWindow lifecycle methods and support MAUI's ConfigureLifecycleEvents builder pattern for platform-specific event hooks.
IWindow Lifecycle
The base app delegates (MacOSMauiApplication, TvOSMauiApplication) automatically call the standard IWindow lifecycle methods:
| IWindow Method | macOS Trigger | tvOS Trigger |
|---|---|---|
Created() |
App launch | App launch |
Activated() |
App becomes active | App becomes active |
Deactivated() |
App loses active status | App resigns activation |
Stopped() |
App hidden (Cmd+H) | App enters background |
Resumed() |
App unhidden | App enters foreground |
Destroying() |
App terminating | App terminating |
Subscribe to these via MAUI's standard Window events:
protected override Window CreateWindow(IActivationState? activationState)
{
var window = new Window(new MainPage());
window.Activated += (s, e) => Console.WriteLine("Window Activated");
window.Stopped += (s, e) => Console.WriteLine("Window Stopped");
return window;
}
ConfigureLifecycleEvents (Platform-Specific)
For platform-specific lifecycle hooks with access to native arguments, use the ConfigureLifecycleEvents builder pattern:
macOS:
builder.ConfigureLifecycleEvents(events =>
{
events.AddMacOS(macOS => macOS
.DidFinishLaunching(notification => { /* NSNotification */ })
.DidBecomeActive(notification => { })
.DidResignActive(notification => { })
.DidHide(notification => { })
.DidUnhide(notification => { })
.WillTerminate(notification => { })
);
});
tvOS:
builder.ConfigureLifecycleEvents(events =>
{
events.AddTvOS(tvOS => tvOS
.FinishedLaunching(app => { /* UIApplication */ })
.OnActivated(app => { })
.OnResignActivation(app => { })
.DidEnterBackground(app => { })
.WillEnterForeground(app => { })
.WillTerminate(app => { })
);
});
BlazorWebView (macOS only)
The BlazorWebView implementation uses a custom MacOSBlazorWebView control instead of the MAUI package's BlazorWebView — the built-in control internally casts its handler to the iOS/Catalyst BlazorWebViewHandler, which fails on AppKit.
Architecture:
MacOSBlazorWebView— a simpleViewsubclass withHostPage,StartPath, andRootComponentspropertiesBlazorWebViewHandler— creates a WKWebView with a customapp://URL scheme handler and injects the Blazor interop scriptMacOSWebViewManager— bridgesWebViewManager(fromMicrosoft.AspNetCore.Components.WebView) to the native WKWebViewMacOSMauiAssetFileProvider— serves static content from the macOS app bundle'sResources/directoryMacOSBlazorDispatcher— bridges MAUI'sIDispatcherto Blazor's abstractDispatcher
Usage:
using Microsoft.Maui.Platform.MacOS.Controls;
var blazorView = new MacOSBlazorWebView
{
HostPage = "wwwroot/index.html",
HeightRequest = 400
};
blazorView.RootComponents.Add(new BlazorRootComponent
{
Selector = "#app",
ComponentType = typeof(MyApp.Components.Counter)
});
Static web assets (wwwroot/) must be included as BundleResource items in the project file, and blazor.modules.json (an empty [] array) plus blazor.webview.js must be present under wwwroot/_framework/.
Essentials
Platform-specific implementations of MAUI Essentials APIs for both tvOS and macOS.
Supported APIs
| API | tvOS | macOS | Notes |
|---|---|---|---|
| AppInfo | ✅ | ✅ | Package name, version, build, theme, layout direction |
| DeviceInfo | ✅ | ✅ | Model, manufacturer, device name, OS version, platform, idiom, device type |
| Connectivity | ✅ | ✅ | Network access status, connection profiles (WiFi), change events |
| Battery | ❌ | ✅ | Charge level, state, power source, change events (IOKit). Not available on tvOS. |
| DeviceDisplay | ✅ | ✅ | Screen dimensions, density, orientation, rotation, refresh rate, keep screen on |
| FileSystem | ✅ | ✅ | Cache directory, app data directory, app package file access |
| Preferences | ✅ | ✅ | Key/value storage via NSUserDefaults |
| SecureStorage | ✅ | ✅ | Encrypted key/value storage via Keychain |
| FilePicker | ❌ | ✅ | Single/multiple file picking via NSOpenPanel. Not available on tvOS. |
| MediaPicker | ❌ | ✅ | Photo/video picking via NSOpenPanel (no capture). Not available on tvOS. |
| TextToSpeech | ✅ | ✅ | macOS: NSSpeechSynthesizer, tvOS: AVSpeechSynthesizer. GetLocalesAsync unavailable on tvOS (AOT). |
| Clipboard | ✅ | ✅ | Copy/paste text. macOS: NSPasteboard, tvOS: in-process (no system pasteboard on tvOS). |
| Browser | ❌ | ✅ | Open URLs in default browser via NSWorkspace. Not available on tvOS. |
| Share | ❌ | ✅ | Share sheet via NSSharingServicePicker. Not available on tvOS. |
| Launcher | ❌ | ✅ | Open files/URIs with default app via NSWorkspace. Not available on tvOS. |
Usage
Call the registration method early in your app startup (before MauiApp.CreateBuilder()):
// macOS
Microsoft.Maui.Essentials.MacOS.EssentialsExtensions.UseMacOSEssentials();
// tvOS
Microsoft.Maui.Essentials.TvOS.EssentialsExtensions.UseTvOSEssentials();
Then use the standard MAUI Essentials APIs:
var appName = AppInfo.Name;
var version = AppInfo.VersionString;
var theme = AppInfo.RequestedTheme;
var model = DeviceInfo.Model;
var platform = DeviceInfo.Platform;
Note: MAUI's
AppInfo.SetCurrent()andDeviceInfo.SetCurrent()areinternal, so the Essentials libraries use reflection to set the backing field. This works with thenet10.0fallback assemblies.
Essentials TODO
- VersionTracking
- MainThread
Dialogs
Dialogs (DisplayAlertAsync, DisplayPromptAsync) are supported on macOS via NSAlert, but are not yet supported on tvOS.
MAUI's dialog system is driven by an internal AlertManager class that resolves an IAlertManagerSubscription implementation from DI. On macOS, we register a custom implementation using DispatchProxy to implement the internal interface via reflection. However, tvOS uses AOT compilation which does not support dynamic code generation — DispatchProxy throws PlatformNotSupportedException at runtime.
Until IAlertManagerSubscription is made public in MAUI (see the proposal), tvOS dialog support is blocked. The implementation is commented out in src/Microsoft.Maui.Platform.TvOS/Platform/AlertManagerSubscription.cs and the sample uses #if !TVOS compiler directives to exclude dialog UI on Apple TV.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-tvos26.0 is compatible. |
-
net10.0-tvos26.0
- Microsoft.Maui.Essentials (>= 10.0.30)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-alpha-0001 | 47 | 2/19/2026 |