maui-accessibility
Guide for making .NET MAUI apps accessible — screen reader support via SemanticProperties, heading levels, AutomationProperties…
Add secure storage to .NET MAUI apps using SecureStorage.Default. Covers SetAsync, GetAsync, Remove, RemoveAll, platform setup (Android backup rules, iOS Keychain entitlements, Windows limits), common pitfalls, and a DI wrapper service for testability. USE FOR: "secure storage",
$ npx -y skills add davidortinau/maui-skills --skill maui-secure-storage --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/maui-secure-storageContext preview
The summary Claude sees to decide when to auto-load this skill.
Add secure storage to .NET MAUI apps using SecureStorage.Default. Covers SetAsync, GetAsync, Remove, RemoveAll, platform setup (Android backup rules, iOS Keychain entitlements, Windows limits), common pitfalls, and a DI wrapper service for testability. USE FOR: "secure storage",
name: maui-secure-storage description: > Add secure storage to .NET MAUI apps using SecureStorage.Default. Covers SetAsync, GetAsync, Remove, RemoveAll, platform setup (Android backup rules, iOS Keychain entitlements, Windows limits), common pitfalls, and a DI wrapper service for testability. USE FOR: "secure storage", "SecureStorage", "store token securely", "Keychain", "Android Keystore", "save secret", "encrypted storage", "store credentials", "sensitive data storage". DO NOT USE FOR: general file storage (use maui-file-handling), SQLite databases (use maui-sqlite-database), or authentication flows (use maui-authentication).
Auto Backup restores encrypted preferences to a new device where the encryption key is invalid — this throws **unrecoverable exceptions**. You must either disable Auto Backup or exclude secure storage files from backup. See `references/secure-storage-api.md` for setup options.
Corrupted values from backup restoration throw exceptions. Never call `GetAsync` unprotected:
// ❌ Unprotected — crashes on corrupted backup data
var value = await SecureStorage.Default.GetAsync("key");
// ✅ Protected — handles corruption gracefully
try
{
var value = await SecureStorage.Default.GetAsync("key");
}
catch (Exception)
{
SecureStorage.Default.RemoveAll();
}Add keychain access groups for Simulator builds, but **remove before physical device / App Store builds** — they cause signing issues on devices where they aren't needed.
Unlike Android, uninstalling an iOS app does **not** remove its Keychain entries. Values persist and are available if the app is reinstalled. Design for this — don't assume a fresh install means empty storage.
Values may sync across devices via iCloud Keychain if the user has it enabled. This is platform behavior, not controllable from MAUI. Don't store device-specific tokens that shouldn't roam.
// ❌ Storing large data — SecureStorage is for small secrets only
await SecureStorage.Default.SetAsync("profile_image", base64EncodedImage);
// ✅ Store tokens, passwords, short secrets
await SecureStorage.Default.SetAsync("auth_token", jwtToken);
// ❌ Logging secret values
_logger.LogInformation("Token: {Token}", await SecureStorage.Default.GetAsync("auth_token"));
// ✅ Log existence, not value
_logger.LogInformation("Token exists: {Exists}", token is not null);
// ❌ Storing complex objects without serialization (values are strings only)
await SecureStorage.Default.SetAsync("user", userObject);
// ✅ Serialize to JSON first
await SecureStorage.Default.SetAsync("user", JsonSerializer.Serialize(user));| Question | Answer | |----------|--------| | Storing a token, password, or API key? | ✅ Use SecureStorage | | Storing user preferences or settings? | ❌ Use `Preferences` instead | | Storing large files or blobs? | ❌ Use file system + encryption | | Need cross-device sync? | ⚠️ iOS syncs via iCloud Keychain automatically | | Need data cleared on uninstall? | ⚠️ Only works on Android, not iOS |
Never call `SecureStorage.Default` directly from ViewModels — wrap it in an `ISecureStorageService` interface for testability. See `references/secure-storage-api.md` for the full DI wrapper pattern with mock examples.
// ❌ Direct static access — untestable
public class LoginViewModel
{
public async Task SaveToken(string token)
=> await SecureStorage.Default.SetAsync("auth_token", token);
}
// ✅ Inject interface — testable and mockable
public class LoginViewModel(ISecureStorageService secure)
{
public async Task SaveToken(string token)
=> await secure.SetAsync("auth_token", token);
}A collection of 41 skills for .NET MAUI development, UI/UX design reasoning, and Xamarin migration, designed for use with GitHub Copilot CLI and Claude Code.
Guide for making .NET MAUI apps accessible — screen reader support via SemanticProperties, heading levels, AutomationProperties…
.NET MAUI view animations, custom animations, easing functions, rotation, scale, translation, and fade effects. USE FOR: "animate…
.NET MAUI app icon configuration, splash screen setup, SVG to PNG conversion at build time, composed/adaptive icons, and…
.NET MAUI app lifecycle guidance covering the four app states (not running, running, deactivated, stopped), cross-platform Window…
Guide for .NET MAUI apps consuming .NET Aspire-hosted backend services. Covers AppHost configuration, service discovery for…
Add authentication to .NET MAUI apps. Covers WebAuthenticator for generic OAuth 2.0 / social login, and MSAL.NET for Microsoft…