Xamarin.Essentials: Permisos

La clase Permissions proporciona la capacidad de comprobar y solicitar permisos en tiempo de ejecución.

Primeros pasos

Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que la biblioteca está correctamente instalada y configurada en los proyectos.

Esta API usa permisos en tiempo de ejecución en Android. Asegúrese de que Xamarin.Essentials se haya inicializado por completo y de que el control de permisos esté configurado en la aplicación.

En el elemento MainLauncher del proyecto de Android o cualquier Activity que se inicie, Xamarin.Essentials se debe inicializar en el método OnCreate:

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
    //...
}    

Para controlar los permisos en tiempo de ejecución de Android, Xamarin.Essentials debe recibir cualquier OnRequestPermissionsResult. Agregue el código siguiente a todas las clases Activity:

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

Uso de permisos

Agregue una referencia a Xamarin.Essentials en la clase:

using Xamarin.Essentials;

Comprobación de permisos

Para comprobar el estado actual de un permiso, use el método CheckStatusAsync junto con el permiso específico para el que obtener el estado.

var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();

Se produce una excepción PermissionException si no se declara el permiso necesario.

Es mejor comprobar el estado del permiso antes de solicitarlo. Cada sistema operativo devuelve un estado predeterminado diferente si nunca se ha solicitado el usuario. iOS devuelve Unknown, mientras que otros devuelven Denied. Si el estado es Granted, no es necesario realizar otras llamadas. En iOS, si el estado es Denied, debe pedir al usuario que cambie el permiso en la configuración, y en Android puede llamar a ShouldShowRationale para detectar si el usuario ya denegó el permiso en el pasado.

Solicitar permisos

Para solicitar un permiso a los usuarios, use el método RequestAsync junto con el permiso específico que se va a solicitar. Si el usuario ha concedido previamente el permiso y no lo ha revocado, este método devuelve Granted inmediatamente y no muestra ningún cuadro de diálogo.

var status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();

Se produce una excepción PermissionException si no se declara el permiso necesario.

Tenga en cuenta que, en algunas plataformas, una solicitud de permiso solo se puede activar una vez. El desarrollador debe controlar otros mensajes para comprobar si un permiso tiene el estado Denied y pedir al usuario que lo active manualmente.

Estado del permiso

Al usar los elementos CheckStatusAsync o RequestAsync, se devuelve un estado PermissionStatus que se puede usar para determinar los pasos siguientes:

  • Desconocido: el permiso tiene un estado desconocido.
  • Denegado: el usuario denegó la solicitud del permiso.
  • Deshabilitado: la característica está deshabilitada en el dispositivo.
  • Concedido: el usuario concedió el permiso o se ha concedido automáticamente.
  • Restringido: en un estado restringido.

Explicación de por qué se necesita el permiso

Es un procedimiento recomendado explicar el motivo por el que la aplicación necesita un permiso específico. En iOS debe especificar una cadena que se muestre al usuario. Android no tiene esta capacidad y, además, el estado de permiso tiene como valor predeterminado Deshabilitado. Esto limita la capacidad de saber si el usuario ha denegado el permiso o si es la primera vez que se le solicita. El método ShouldShowRationale se puede usar para determinar si se debe mostrar una interfaz de usuario educativa. Si el método devuelve true, esto se debe a que el usuario ha denegado o deshabilitado el permiso en el pasado. Otras plataformas siempre devolverán false cuando se llame a este método.

Permisos disponibles

Xamarin.Essentials intenta abstraer tantos permisos como sea posible. Pero cada sistema operativo tiene un conjunto diferente de permisos en tiempo de ejecución. Además, hay diferencias a la hora de proporcionar una única API para algunos permisos. Esta es una guía para los permisos disponibles actualmente:

Guía de iconos:

  • Totalmente compatible - Compatible
  • No compatible - No compatible o requerido
Permiso Android iOS UWP watchOS tvOS Tizen
CalendarRead Compatible con Android Compatible con iOS No compatible con UWP Compatible con watchOS No compatible con tvOS No compatible con Tizen
CalendarWrite Compatible con Android Compatible con iOS No compatible con UWP Compatible con watchOS No compatible con tvOS No compatible con Tizen
Cámara Compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS Compatible con Tizen
ContactsRead Compatible con Android Compatible con iOS Compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
ContactsWrite Compatible con Android Compatible con iOS Compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
Linterna Compatible con Android No compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS Compatible con Tizen
LocationWhenInUse Compatible con Android Compatible con iOS Compatible con UWP Compatible con watchOS Compatible con tvOS Compatible con Tizen
LocationAlways Compatible con Android Compatible con iOS Compatible con UWP Compatible con watchOS No compatible con tvOS Compatible con Tizen
Medios No compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
Micrófono Compatible con Android Compatible con iOS Compatible con UWP No compatible con watchOS No compatible con tvOS Compatible con Tizen
Teléfono Compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
Fotos No compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS Compatible con tvOS No compatible con Tizen
Recordatorios No compatible con Android Compatible con iOS No compatible con UWP Compatible con watchOS No compatible con tvOS No compatible con Tizen
Sensores Compatible con Android Compatible con iOS Compatible con UWP Compatible con watchOS No compatible con tvOS No compatible con Tizen
SMS Compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
Voz Compatible con Android Compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
StorageRead Compatible con Android No compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen
StorageWrite Compatible con Android No compatible con iOS No compatible con UWP No compatible con watchOS No compatible con tvOS No compatible con Tizen

Si un permiso está marcado como no compatible siempre devolverá Granted cuando se compruebe o solicite.

Uso general

El código siguiente presenta el patrón de uso general para determinar si un permiso se ha concedido, o para solicitarlo, en el caso de que todavía no se haya hecho. Este código usa características que están disponibles con la versión 1.6.0 de Xamarin.Essentials y las posteriores.

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

Cada tipo de permiso puede tener una instancia de este creada, para que se pueda llamar directamente a los métodos.

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

Extensión de permisos

La API Permissions se creó para aportar flexibilidad y extensibilidad a las aplicaciones que requieren validación o permisos adicionales que no están incluidos en Xamarin.Essentials. Cree una clase que herede de BasePermission e implemente los métodos abstractos necesarios.

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

Al implementar un permiso en una plataforma específica, se puede heredar de la clase BasePlatformPermission. Esto proporciona métodos auxiliares de la plataforma adicionales para comprobar automáticamente las declaraciones. Esto puede ayudar a la hora de crear permisos personalizados para realizar agrupaciones. Por ejemplo, puede solicitar acceso Leer y Escribir en el almacenamiento en Android con el siguiente permiso personalizado.

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

Después, puede llamar al nuevo permiso desde el proyecto de Android.

await Permissions.RequestAsync<ReadWriteStoragePermission>();

Si quiere llamar a esta API desde el código compartido, puede crear una interfaz y usar un servicio de dependencia para efectuar el registro y obtener la implementación.

public interface IReadWritePermission
{        
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}

Después, implemente la interfaz en el proyecto de la plataforma:

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

Luego, puede registrar la implementación específica:

DependencyService.Register<IReadWritePermission, ReadWriteStoragePermission>();

A continuación, desde el proyecto compartido, puede resolverla y usarla:

var readWritePermission = DependencyService.Get<IReadWritePermission>();
var status = await readWritePermission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
    status = await readWritePermission.RequestAsync();
}

Detalles de implementación de la plataforma

Los permisos deben tener los atributos coincidentes establecidos en el archivo de manifiesto de Android. El estado de permiso tiene como valor predeterminado Denegado.

Obtenga más información en el documento Permisos en Xamarin.Android.

API

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.