Поділитися через


Xamarin.Essentials: Permissions

The Permissions class provides the ability to check and request runtime permissions.

Get started

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);
}

Using Permissions

Add a reference to Xamarin.Essentials in your class:

using Xamarin.Essentials;

Checking Permissions

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.

Requesting Permissions

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.

Permission Status

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

Explain Why Permission Is Needed

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.

Available Permissions

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:

  • Full Support - Supported
  • Not Supported - Not supported/required
Permission Android iOS UWP watchOS tvOS Tizen
CalendarRead Android supported iOS supported UWP not supported watchOS supported tvOS not supported Tizen not supported
CalendarWrite Android supported iOS supported UWP not supported watchOS supported tvOS not supported Tizen not supported
Camera Android supported iOS supported UWP not supported watchOS not supported tvOS not supported Tizen  supported
ContactsRead Android supported iOS supported UWP supported watchOS not supported tvOS not supported Tizen not supported
ContactsWrite Android supported iOS supported UWP supported watchOS not supported tvOS not supported Tizen not supported
Flashlight Android supported iOS not supported UWP not supported watchOS not supported tvOS not supported Tizen  supported
LocationWhenInUse Android supported iOS supported UWP supported watchOS supported tvOS supported Tizen  supported
LocationAlways Android supported iOS supported UWP supported watchOS supported tvOS not supported Tizen  supported
Media Android not supported iOS supported UWP not supported watchOS not supported tvOS not supported Tizen not supported
Microphone Android supported iOS supported UWP supported watchOS not supported tvOS not supported Tizen  supported
Phone Android supported iOS supported UWP not supported watchOS not supported tvOS not supported Tizen not supported
Photos Android not supported iOS supported UWP not supported watchOS not supported tvOS supported Tizen not supported
Reminders Android not supported iOS supported UWP not supported watchOS supported tvOS not supported Tizen not supported
Sensors Android supported iOS supported UWP supported watchOS supported tvOS not supported Tizen not supported
Sms Android supported iOS supported UWP not supported watchOS not supported tvOS not supported Tizen not supported
Speech Android supported iOS supported UWP not supported watchOS not supported tvOS not supported Tizen not supported
StorageRead Android supported iOS not supported UWP not supported watchOS not supported tvOS not supported Tizen not supported
StorageWrite Android supported iOS not supported UWP not supported watchOS not supported tvOS not supported Tizen not supported

If a permission is marked as not supported it will always return Granted when checked or requested.

General Usage

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;
}

Extending Permissions

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();
}

Platform Implementation Specifics

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.

API

Find more Xamarin videos on Channel 9 and YouTube.