Xamarin.Essentials: Permissions
The Permissions class provides the ability to check and request runtime permissions.
To start using this API, read the getting started guide for Xamarin.Essentials to ensure the library is properly installed and set up in your projects.
This API uses runtime permissions on Android. Please ensure that Xamarin.Essentials is fully initialized and permission handling is setup in your app.
In the Android project's MainLauncher
or any Activity
that is launched Xamarin.Essentials must be initialized in the OnCreate
method:
protected override void OnCreate(Bundle savedInstanceState)
{
//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
//...
}
To handle runtime permissions on Android, Xamarin.Essentials must receive any OnRequestPermissionsResult
. Add the following code to all Activity
classes:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
Add a reference to Xamarin.Essentials in your class:
using Xamarin.Essentials;
To check the current status of a permission, use the CheckStatusAsync
method along with the specific permission to get the status for.
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
A PermissionException
is thrown if the required permission is not declared.
It's best to check the status of the permission before requesting it. Each operating system returns a different default state if the user has never been prompted. iOS returns Unknown
, while others return Denied
. If the status is Granted
then there is no need to make other calls. On iOS if the status is Denied
you should prompt the user to change the permission in the settings and on Android you can call ShouldShowRationale
to detect if the user has already denied the permission in the past.
To request a permission from the users, use the RequestAsync
method along with the specific permission to request. If the user previously granted permission and hasn't revoked it, then this method will return Granted
immediately and not display a dialog.
var status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
A PermissionException
is thrown if the required permission is not declared.
Note, that on some platforms a permission request can only be activated a single time. Further prompts must be handled by the developer to check if a permission is in the Denied
state and ask the user to manually turn it on.
When using CheckStatusAsync
or RequestAsync
a PermissionStatus
will be returned that can be used to determine the next steps:
- Unknown - The permission is in an unknown state
- Denied - The user denied the permission request
- Disabled - The feature is disabled on the device
- Granted - The user granted permission or is automatically granted
- Restricted - In a restricted state
It is best practice to explain why your application needs a specific permission. On iOS you must specify a string that is displayed to the user. Android does not have this ability and and also defaults permission status to Disabled. This limits the ability to know if the user denied the permission or if it is the first time prompting the user. The ShouldShowRationale
method can be used to determine if an educational UI should be displayed. If the method returns true
this is because the user has denied or disabled the permission in the past. Other platforms will always return false
when calling this method.
Xamarin.Essentials attempts to abstract as many permissions as possible. However, each operating system has a different set of runtime permissions. In addition there are differences when providing a single API for some permissions. Here is a guide to the currently available permissions:
Icon Guide:
- - Supported
- - Not supported/required
Permission | Android | iOS | UWP | watchOS | tvOS | Tizen |
---|---|---|---|---|---|---|
CalendarRead | ||||||
CalendarWrite | ||||||
Camera | ||||||
ContactsRead | ||||||
ContactsWrite | ||||||
Flashlight | ||||||
LocationWhenInUse | ||||||
LocationAlways | ||||||
Media | ||||||
Microphone | ||||||
Phone | ||||||
Photos | ||||||
Reminders | ||||||
Sensors | ||||||
Sms | ||||||
Speech | ||||||
StorageRead | ||||||
StorageWrite |
If a permission is marked as it will always return Granted
when checked or requested.
The following code presents the general usage pattern for determining whether a permission has been granted and requesting it if it has not. This code uses features that are available with Xamarin.Essentials version 1.6.0 or later.
public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
{
// Prompt the user to turn on in settings
// On iOS once a permission has been denied it may not be requested again from the application
return status;
}
if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>())
{
// Prompt the user with additional information as to why the permission is needed
}
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
return status;
}
Each permission type can have an instance of it created that the methods can be called directly.
public async Task GetLocationAsync()
{
var status = await CheckAndRequestPermissionAsync(new Permissions.LocationWhenInUse());
if (status != PermissionStatus.Granted)
{
// Notify user permission was denied
return;
}
var location = await Geolocation.GetLocationAsync();
}
public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>(T permission)
where T : BasePermission
{
var status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await permission.RequestAsync();
}
return status;
}
The Permissions API was created to be flexible and extensible for applications that require additional validation or permissions that aren't included in Xamarin.Essentials. Create a new class that inherits from BasePermission
and implement the required abstract methods.
public class MyPermission : BasePermission
{
// This method checks if current status of the permission
public override Task<PermissionStatus> CheckStatusAsync()
{
throw new System.NotImplementedException();
}
// This method is optional and a PermissionException is often thrown if a permission is not declared
public override void EnsureDeclared()
{
throw new System.NotImplementedException();
}
// Requests the user to accept or deny a permission
public override Task<PermissionStatus> RequestAsync()
{
throw new System.NotImplementedException();
}
}
When implementing a permission in a specific platform, the BasePlatformPermission
class can be inherited from. This provides additional platform helper methods to automatically check the declarations. This can help when creating custom permissions that do groupings. For example, you can request both Read and Write access to storage on Android using the following custom permission.
public class ReadWriteStoragePermission : Xamarin.Essentials.Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
{
(Android.Manifest.Permission.ReadExternalStorage, true),
(Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
Then you can call your new permission from Android project.
await Permissions.RequestAsync<ReadWriteStoragePermission>();
If you wanted to call this API from your shared code you could create an interface and use a dependency service to register and get the implementation.
public interface IReadWritePermission
{
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
Then implement the interface in your platform project:
public class ReadWriteStoragePermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IReadWritePermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
{
(Android.Manifest.Permission.ReadExternalStorage, true),
(Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
You can then register the specific implementation:
DependencyService.Register<IReadWritePermission, ReadWriteStoragePermission>();
Then from your shared project you can resolve and use it:
var readWritePermission = DependencyService.Get<IReadWritePermission>();
var status = await readWritePermission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await readWritePermission.RequestAsync();
}
Permissions must have the matching attributes set in the Android Manifest file. Permission status defaults to Denied.
Read more on the Permissions in Xamarin.Android documentation.