{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/maui-app-lifecycle",
  "version": "1.0.1",
  "name": "maui-app-lifecycle",
  "description": ".NET MAUI app lifecycle guidance — the four app states, cross-platform Window lifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying), platform-specific lifecycle mapping, backgrounding and resume behavior, and state-preservation patterns. USE FOR: \"app lifecycle\", \"window lifecycle events\", \"save state on background\", \"resume app\", \"OnStopped\", \"OnResumed\", \"backgrounding\", \"deactivated event\", \"ConfigureLifecycleEvents\", \"platform lifecycle hooks\". DO NOT USE FOR: navigation events (use maui-shell-navigation), dependency injection setup (use maui-dependency-injection), platform API invocation (use conditional compilation and partial classes).",
  "system_prompt_fragment": "# .NET MAUI App Lifecycle\n\nHandle application state transitions correctly in .NET MAUI. This skill covers the cross-platform Window lifecycle events, their platform-native mappings, and patterns for preserving state across backgrounding and resume cycles.\n\n## When to Use\n\n- Saving or restoring state when the app backgrounds or resumes\n- Subscribing to Window lifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying)\n- Hooking into platform-native lifecycle callbacks via `ConfigureLifecycleEvents`\n- Deciding where to place initialization, teardown, or refresh logic\n- Understanding the difference between Deactivated and Stopped\n\n## When Not to Use\n\n- Page-level navigation events — use Shell navigation guidance instead\n- Registering services at startup — use dependency injection guidance instead\n- Calling platform-specific APIs outside lifecycle context — use platform invoke guidance instead\n\n## Inputs\n\n- The target lifecycle transition (e.g., \"save draft when backgrounded\", \"refresh data on resume\")\n- Which platforms the developer targets (Android, iOS, Mac Catalyst, Windows)\n- Whether the app uses multiple windows (iPad, Mac Catalyst, desktop Windows)\n\n## App States\n\nA .NET MAUI app moves through four states:\n\n| State | Description |\n|---|---|\n| **Not Running** | Process does not exist |\n| **Running** | Foreground, receiving input |\n| **Deactivated** | Visible but lost focus (dialog, split-screen, notification shade) |\n| **Stopped** | Fully backgrounded, UI not visible |\n\nTypical flow: Not Running → Running → Deactivated → Stopped → Running (resumed) or Not Running (terminated).\n\n## Window Lifecycle Events\n\n`Microsoft.Maui.Controls.Window` exposes six cross-platform events:\n\n| Event | Fires when |\n|---|---|\n| `Created` | Native window allocated |\n| `Activated` | Window receives input focus |\n| `Deactivated` | Window loses focus (may still be visible) |\n| `Stopped` | Window is no longer visible |\n| `Resumed` | Window returns to foreground after Stopped |\n| `Destroying` | Native window is being torn down |\n\n### Subscribing via CreateWindow\n\nOverride `CreateWindow` in your `App` class and attach event handlers:\n\n```csharp\npublic partial class App : Application\n{\n    protected override Window CreateWindow(IActivationState? activationState)\n    {\n        var window = base.CreateWindow(activationState);\n\n        window.Created += (s, e) => Debug.WriteLine(\"Created\");\n        window.Activated += (s, e) => Debug.WriteLine(\"Activated\");\n        window.Deactivated += (s, e) => Debug.WriteLine(\"Deactivated\");\n        window.Stopped += (s, e) => Debug.WriteLine(\"Stopped\");\n        window.Resumed += (s, e) => Debug.WriteLine(\"Resumed\");\n        window.Destroying += (s, e) => Debug.WriteLine(\"Destroying\");\n\n        return window;\n    }\n}\n```\n\n### Subscribing via a Custom Window Subclass\n\nCreate a `Window` subclass and override the virtual methods:\n\n```csharp\npublic class AppWindow : Window\n{\n    public AppWindow(Page page) : base(page) { }\n\n    protected override void OnActivated() { /* refresh UI */ }\n    protected override void OnStopped() { /* save state */ }\n    protected override void OnResumed() { /* restore state */ }\n    protected override void OnDestroying() { /* cleanup */ }\n}\n```\n\nReturn it from `CreateWindow`:\n\n```csharp\nprotected override Window CreateWindow(IActivationState? activationState)\n    => new AppWindow(new AppShell());\n```\n\n## Workflow: Save and Restore State on Background\n\n1. **Identify transient state** — draft text, scroll position, form inputs, timer values.\n2. **Save in `OnStopped`** — use `Preferences` for small values or file serialization for larger state.\n3. **Restore in `OnResumed`** — read back saved values and apply to your view model.\n4. **Also save in `OnDestroying`** on Android — the back button can skip `Stopped` entirely.\n5. **Keep handlers fast** — complete within 1–2 seconds to avoid ANR on Android or watchdog kills on iOS.\n\n```csharp\nprotected override void OnStopped()\n{\n    base.OnStopped();\n    Preferences.Set(\"draft_text\", _viewModel.DraftText);\n    Preferences.Set(\"scroll_y\", _viewModel.ScrollY);\n}\n\nprotected override void OnResumed()\n{\n    base.OnResumed();\n    _viewModel.DraftText = Preferences.Get(\"draft_text\", string.Empty);\n    _viewModel.ScrollY = Preferences.Get(\"scroll_y\", 0.0);\n}\n\nprotected override void OnDestroying()\n{\n    base.OnDestroying();\n    // Android back-button can skip Stopped\n    Preferences.Set(\"draft_text\", _viewModel.DraftText);\n}\n```\n\n## Platform Lifecycle Mapping\n\n### Android\n\n| Window Event | Android Callback |\n|---|---|\n| Created | `OnCreate` |\n| Activated | `OnResume` |\n| Deactivated | `OnPause` |\n| Stopped | `OnStop` |\n| Resumed | `OnRestart` → `OnStart` → `OnResume` |\n| Destroying | `OnDestroy` |\n\n### iOS / Mac Catalyst\n\n| Window Event | UIKit Callback |\n|---|---|\n| Created | `WillFinishLaunching` / `SceneWillConnect` |\n| Activated | `DidBecomeActive` |\n| Deactivated | `WillResignActive` |\n| Stopped | `DidEnterBackground` |\n| Resumed | `WillEnterForeground` |\n| Destroying | `WillTerminate` |\n\n### Windows (WinUI)\n\n| Window Event | WinUI Callback |\n|---|---|\n| Created | `OnLaunched` |\n| Activated | `Activated` (foreground) |\n| Deactivated | `Activated` (background) |\n| Stopped | `VisibilityChanged` (false) |\n| Resumed | `VisibilityChanged` (true) |\n| Destroying | `Closed` |\n\n## Hooking Native Lifecycle Directly\n\nUse `ConfigureLifecycleEvents` in `MauiProgram.cs` when you need platform-specific callbacks beyond what Window events provide:\n\n```csharp\nbuilder.ConfigureLifecycleEvents(events =>\n{\n#if ANDROID\n    events.AddAndroid(android => android\n        .OnCreate((activity, bundle) => Debug.WriteLine(\"Android OnCreate\"))\n        .OnResume(activity => Debug.WriteLine(\"Android OnResume\"))\n        .OnPause(activity => Debug.WriteLine(\"Android OnPause\"))\n        .OnStop(activity => Debug.WriteLine(\"Android OnStop\"))\n        .OnDestroy(activity => Debug.WriteLine(\"Android OnDestroy\")));\n#elif IOS || MACCATALYST\n    events.AddiOS(ios => ios\n        .DidBecomeActive(app => Debug.WriteLine(\"iOS DidBecomeActive\"))\n        .WillResignActive(app => Debug.WriteLine(\"iOS WillResignActive\"))\n        .DidEnterBackground(app => Debug.WriteLine(\"iOS DidEnterBackground\"))\n        .WillEnterForeground(app => Debug.WriteLine(\"iOS WillEnterForeground\")));\n#elif WINDOWS\n    events.AddWindows(windows => windows\n        .OnLaunched((app, args) => Debug.WriteLine(\"Windows OnLaunched\"))\n        .OnActivated((window, args) => Debug.WriteLine(\"Windows Activated\"))\n        .OnClosed((window, args) => Debug.WriteLine(\"Windows Closed\")));\n#endif\n});\n```\n\n## Common Pitfalls\n\n1. **Resumed does not fire on first launch.** The initial sequence is `Created` → `Activated`. Use `OnActivated` for logic that must run on every foreground entry, not `OnResumed`.\n\n2. **Deactivated ≠ Stopped.** A dialog, split-screen, or notification pull-down triggers `Deactivated` without `Stopped`. Do not perform heavy saves in `OnDeactivated` — the app may never actually background.\n\n3. **Android back button skips Stopped.** On Android, pressing back may call `Destroying` directly without `Stopped`. Place critical save logic in both `OnStopped` and `OnDestroying`.\n\n4. **Multi-window apps fire events independently.** On iPad, Mac Catalyst, and desktop Windows each `Window` instance fires its own lifecycle events. Do not assume a single global lifecycle.\n\n5. **Long-running handlers cause kills.** Android enforces a ~5 second ANR timeout; iOS has limited background execution time. Keep lifecycle handlers synchronous and fast — use `Preferences` for quick saves, not database writes.\n\n6. **Do not use legacy Xamarin.Forms lifecycle methods.** `Application.OnStart()`, `Application.OnSleep()`, and `Application.OnResume()` exist for backward compatibility but bypass Window-level events. In .NET MAUI, prefer `Window` lifecycle events (`OnActivated`, `OnStopped`, `OnResumed`, etc.) for correct multi-window behavior.",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/maui-app-lifecycle"
  ],
  "tags": [
    "dotnet-maui",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-maui/skills/maui-app-lifecycle/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-maui/skills/maui-app-lifecycle/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}