This tutorial explores how to access platform-specific features and APIs within your .NET MAUI applications. While .NET MAUI provides a unified API surface for common UI and device functionalities, there are times when you need to leverage unique capabilities of Android, iOS, macOS, or Windows.
The simplest way to conditionally include or exclude code based on the target platform is using preprocessor directives. These directives are evaluated at compile time.
#if ANDROID
// Code specific to Android
using Android.Widget;
void ShowToast(string message)
{
// Example of an Android-specific API
Toast.MakeText(Android.App.Application.Context, message, ToastLength.Short).Show();
}
#elif IOS
// Code specific to iOS
using UIKit;
void ShowAlert(string message)
{
// Example of an iOS-specific API
var alertController = UIAlertController.Create("Info", message, UIAlertControllerStyle.Alert);
var window = UIApplication.SharedApplication.KeyWindow;
if (window?.RootViewController != null)
{
window.RootViewController.PresentViewController(alertController, true, null);
}
}
#elif MACCATALYST
// Code specific to macOS (Catalyst)
using AppKit;
void ShowNotification(string message)
{
// Example of a macOS-specific API
var notification = new NSUserNotification
{
Title = "MAUI Notification",
InformativeText = message
};
NSUserNotificationCenter.DefaultUserNotificationCenter.Deliver(notification);
}
#elif WINDOWS
// Code specific to Windows
using Microsoft.UI.Xaml.Controls;
void ShowInfoBar(string message)
{
// Example of a Windows-specific API
// This would typically require access to a specific UI element
// For demonstration, imagine this is called within a Page context.
// var page = Window.Current.Content as Microsoft.UI.Xaml.Controls.Page;
// if (page?.FindName("MyInfoBar") is InfoBar infoBar)
// {
// infoBar.Message = message;
// infoBar.IsOpen = true;
// }
}
#endif
.NET MAUI officially recommends using the newer Handler architecture or Interfaces and Implementations pattern. However, for some scenarios, DependencyService might still be encountered or considered.
The DependencyService
is a cross-platform service locator that allows your shared code to discover and invoke platform-specific implementations of an interface.
public interface IPlatformInfo
{
string GetPlatformName();
}
Android:
using Android.App;
using Android.OS;
using YourAppNamespace; // Replace with your app's namespace
[assembly: Xamarin.Forms.Dependency(typeof(YourAppNamespace.Droid.PlatformInfo_Android))]
namespace YourAppNamespace.Droid
{
public class PlatformInfo_Android : IPlatformInfo
{
public string GetPlatformName()
{
return $"Android {Build.VERSION.Release}";
}
}
}
iOS:
using Foundation;
using UIKit;
using YourAppNamespace; // Replace with your app's namespace
[assembly: Xamarin.Forms.Dependency(typeof(YourAppNamespace.iOS.PlatformInfo_iOS))]
namespace YourAppNamespace.iOS
{
public class PlatformInfo_iOS : IPlatformInfo
{
public string GetPlatformName()
{
return $"iOS {UIDevice.CurrentDevice.SystemVersion}";
}
}
}
using Xamarin.Forms;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var platformInfo = DependencyService.Get<IPlatformInfo>();
var platformName = platformInfo.GetPlatformName();
// Display platformName in a Label or other UI element
System.Diagnostics.Debug.WriteLine($"Current Platform: {platformName}");
}
}
This pattern is more aligned with modern .NET MAUI development and offers better testability and modularity compared to DependencyService
.
namespace YourAppNamespace.Services
{
public interface IClipboardService
{
Task SetTextAsync(string text);
Task<string> GetTextAsync();
}
}
Android:
using Android.Content;
using YourAppNamespace.Services;
namespace YourAppNamespace.Platforms.Android
{
public class ClipboardService_Android : IClipboardService
{
public Task<string> GetTextAsync()
{
var clipboardManager = (ClipboardManager)Platform.AppContext.GetSystemService(Context.ClipboardService);
if (clipboardManager?.HasPrimaryClip == true)
{
var clipData = clipboardManager.PrimaryClip;
if (clipData?.ItemCount > 0)
{
return Task.FromResult(clipData.GetItemAt(0)?.Text);
}
}
return Task.FromResult<string>(null);
}
public Task SetTextAsync(string text)
{
var clipboardManager = (ClipboardManager)Platform.AppContext.GetSystemService(Context.ClipboardService);
var clipData = ClipData.NewPlainText("text/plain", text);
clipboardManager?.SetPrimaryClip(clipData);
return Task.CompletedTask;
}
}
}
iOS:
using Foundation;
using UIKit;
using YourAppNamespace.Services;
namespace YourAppNamespace.Platforms.iOS
{
public class ClipboardService_iOS : IClipboardService
{
public Task<string> GetTextAsync()
{
return Task.FromResult(UIPasteboard.General.String);
}
public Task SetTextAsync(string text)
{
UIPasteboard.General.String = text;
return Task.CompletedTask;
}
}
}
macOS:
using AppKit;
using YourAppNamespace.Services;
namespace YourAppNamespace.Platforms.MacCatalyst
{
public class ClipboardService_MacCatalyst : IClipboardService
{
public Task<string> GetTextAsync()
{
return Task.FromResult(NSPasteboard.General.GetStringForType(NSPasteboard.NSPasteboardTypeString));
}
public Task SetTextAsync(string text)
{
NSPasteboard.General.ClearContents();
NSPasteboard.General.SetStringForType(text, NSPasteboard.NSPasteboardTypeString);
return Task.CompletedTask;
}
}
}
Windows:
using Windows.ApplicationModel.DataTransfer;
using System.Threading.Tasks;
using YourAppNamespace.Services;
namespace YourAppNamespace.Platforms.Windows
{
public class ClipboardService_Windows : IClipboardService
{
public async Task<string> GetTextAsync()
{
if (DataPackageView.GetTextAsync().Status == Windows.Foundation.AsyncStatus.Completed)
{
return await DataPackageView.GetTextAsync();
}
return null;
}
public Task SetTextAsync(string text)
{
var dataPackage = new DataPackage();
dataPackage.SetText(text);
Clipboard.SetContent(dataPackage);
return Task.CompletedTask;
}
}
}
In your MauiProgram.cs
, register the platform-specific implementations:
using YourAppNamespace.Services;
using YourAppNamespace.Platforms.Android; // For Android
using YourAppNamespace.Platforms.iOS; // For iOS
using YourAppNamespace.Platforms.MacCatalyst; // For MacCatalyst
using YourAppNamespace.Platforms.Windows; // For Windows
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers(handlers =>
{
// Register platform-specific implementations for IClipboardService
handlers.AddTransient<IClipboardService, ClipboardService_Android>(); // Android
handlers.AddTransient<IClipboardService, ClipboardService_iOS>(); // iOS
handlers.AddTransient<IClipboardService, ClipboardService_MacCatalyst>(); // macOS
handlers.AddTransient<IClipboardService, ClipboardService_Windows>(); // Windows
});
return builder.Build();
}
}
In your ViewModels or Code-Behind, inject or retrieve the service:
using YourAppNamespace.Services;
public class MyViewModel : BindableObject
{
private readonly IClipboardService _clipboardService;
public MyViewModel(IClipboardService clipboardService)
{
_clipboardService = clipboardService;
}
public async Task CopyToClipboard(string text)
{
await _clipboardService.SetTextAsync(text);
}
public async Task<string> PasteFromClipboard()
{
return await _clipboardService.GetTextAsync();
}
}
This is the modern, extensible approach in .NET MAUI for customizing platform controls. You create platform-specific handlers that modify the native controls.
This is more advanced and typically used for custom UI elements. For accessing simple platform APIs, the Interfaces and Implementations pattern is often sufficient and easier to manage.
When accessing platform APIs, always consider: