100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Permissions and Lifecycle

Understand runtime permission requests and the app lifecycle events that govern how a .NET MAUI app behaves in the foreground and background.

Navigation & PlatformIntermediate10 min readJul 10, 2026
Analogies

Requesting Runtime Permissions

Modern Android and iOS both require sensitive capabilities — location, camera, contacts, notifications — to be requested from the user at runtime, not just declared once in a manifest. MAUI wraps this in the Permissions class: await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>() reads the current state without prompting, while await Permissions.RequestAsync<Permissions.LocationWhenInUse>() triggers the native OS dialog if the status isn't already Granted, and both return a PermissionStatus enum (Granted, Denied, Disabled, Restricted, or Unknown).

🏏

Cricket analogy: This is like a broadcaster needing fresh accreditation approval before entering the players' viewing area for each new tournament, even if they held a pass for a previous series — declaring intent once in advance isn't enough.

csharp
public async Task<bool> EnsureLocationPermissionAsync()
{
    var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();

    if (status != PermissionStatus.Granted)
    {
        status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
    }

    if (status != PermissionStatus.Granted)
    {
        await Shell.Current.DisplayAlert(
            "Permission Required",
            "Location access is needed to show nearby stores.",
            "OK");
        return false;
    }

    return true;
}

On iOS, each permission also needs a usage-description string in Info.plist (e.g. NSLocationWhenInUseUsageDescription) — without it, RequestAsync fails immediately or the app crashes when the OS tries to show a prompt with no message to display.

App Lifecycle States

A MAUI app moves through states exposed on the Window/Application object: Created fires once at startup, Activated when it becomes the foreground focused app, Deactivated when it loses focus but is still partially visible (e.g. a system dialog appears over it), Stopped when it's fully backgrounded, and Resumed when it returns to the foreground after being stopped. You subscribe to these in App.xaml.cs or a custom Window subclass by overriding CreateWindow and hooking Window.Activated, Window.Deactivated, Window.Stopped, and Window.Resumed.

🏏

Cricket analogy: This is like a player's on-field status cycling through warming up (Created), actively fielding (Activated), briefly distracted during a drinks break (Deactivated), sitting in the dugout during an innings break (Stopped), and returning to the field after the break (Resumed).

csharp
public partial class App : Application
{
    protected override Window CreateWindow(IActivationState? activationState)
    {
        var window = new Window(new AppShell());

        window.Activated += (s, e) => Console.WriteLine("App activated");
        window.Deactivated += (s, e) => Console.WriteLine("App deactivated");
        window.Stopped += (s, e) => Console.WriteLine("App stopped (backgrounded)");
        window.Resumed += (s, e) => Console.WriteLine("App resumed from background");

        return window;
    }
}

Saving and Restoring State

Because the OS can terminate a backgrounded app to reclaim memory at any time, critical in-progress state — a partially filled form, an in-progress upload's identifier — should be persisted during Stopped rather than assumed to survive, typically to Preferences for small values or a local SQLite database for structured data. On Resumed, the app should check whether it needs to restore that state or refresh stale data, since a significant amount of time (and possibly a network change) may have passed while backgrounded.

🏏

Cricket analogy: This is like a scorer writing down the exact ball-by-ball state before rain stops play, so when play resumes, the match picks up from the precise over and score instead of guessing.

Don't rely on in-memory static fields or singleton state surviving a Stopped state on Android — the OS can and does kill backgrounded processes to reclaim memory, and when the user returns, MAUI creates a fresh process that replays Created rather than truly "waking up" the old one. Persist anything that must survive to Preferences, SecureStorage, or a local database.

  • Permissions.CheckStatusAsync reads current status; Permissions.RequestAsync prompts if not already granted.
  • PermissionStatus can be Granted, Denied, Disabled, Restricted, or Unknown.
  • iOS requires an Info.plist usage-description string for each permission or the request fails/crashes.
  • App lifecycle states are Created, Activated, Deactivated, Stopped, and Resumed, exposed on the Window object.
  • Deactivated means partial focus loss (e.g. a system dialog); Stopped means fully backgrounded.
  • Critical state should be persisted during Stopped since the OS may kill the process entirely.
  • On Resumed, check for stale data or restore saved state rather than assuming nothing changed while backgrounded.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#NETMAUIStudyNotes#PermissionsAndLifecycle#Permissions#Lifecycle#Requesting#Runtime#StudyNotes#SkillVeris#ExamPrep