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.
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).
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
1. What does Permissions.RequestAsync<T>() do if the permission status is already Granted?
2. What additional requirement does iOS have beyond calling Permissions.RequestAsync in code?
3. What is the difference between the Deactivated and Stopped lifecycle states?
4. Why shouldn't critical in-progress state be kept only in static in-memory fields while an app is backgrounded?
Was this page helpful?
You May Also Like
Accessing Device Features
Explore the Microsoft.Maui.Essentials device APIs for geolocation, camera, sensors, and connectivity in .NET MAUI.
Pages and Modals
Understand the ContentPage lifecycle and how modal navigation differs from Shell's normal push navigation in .NET MAUI.
Platform-Specific Code
Learn how to branch behavior per platform in .NET MAUI using conditional compilation, partial classes, and platform folders.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics