Compartir a través de


Permisos

Examinar ejemplo. Examinar el ejemplo

En este artículo se describe cómo puedes usar la clase de .NET Multi-platform App UI (.NET MAUI) Permissions. Esta clase permite comprobar y solicitar permisos en tiempo de ejecución. El tipo Permissions está disponible en el espacio de nombres Microsoft.Maui.ApplicationModel.

Permisos disponibles

.NET MAUI intenta abstraer tantos permisos como sea posible. Pero cada sistema operativo tiene un conjunto diferente de permisos. Aunque la API permite el acceso a un permiso común, puede haber diferencias entre los sistemas operativos relacionados con él. En la tabla siguiente se describen los permisos disponibles:

En la tabla siguiente se usa ✔️ para indicar que se admite el permiso y ❌ para mostrar que no se admite o no es necesario:

Permiso Android iOS Windows tvOS
Batería ✔️
Bluetooth ✔️
CalendarRead ✔️ ✔️
CalendarWrite ✔️ ✔️
Camera ✔️ ✔️
ContactsRead ✔️ ✔️
ContactsWrite ✔️ ✔️
Linterna ✔️
LocationWhenInUse ✔️ ✔️ ✔️
LocationAlways ✔️ ✔️
Media ✔️
Microphone ✔️ ✔️
NearbyWifiDevices ✔️
NetworkState ✔️
Phone ✔️ ✔️
Photos ✔️ ✔️
PhotosAddOnly ✔️ ✔️
PostNotification ✔️
Reminders ✔️
Sensors ✔️ ✔️
Sms ✔️ ✔️
Speech ✔️ ✔️
StorageRead ✔️
StorageWrite ✔️
Vibración ✔️

Importante

Los permisos StorageRead y StorageWrite siempre se devolverán Granted en la API de Android 33+. Esto se debe a que los permisos y WRITE_EXTERNAL_STORAGE Android READ_EXTERNAL_STORAGE subyacentes ya no están disponibles en la API 33.

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

Comprobación de permisos

Para comprobar el estado actual de un permiso, usa el método Permissions.CheckStatusAsync junto con el permiso específico para el que obtener el estado. El siguiente ejemplo comprueba el estado del permiso LocationWhenInUse:

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

Se produce una 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, debes pedir al usuario que cambie el permiso en la configuración. En Android, puede llamar ShouldShowRationale a para detectar si el usuario ya ha denegado el permiso en el pasado.

Estado del permiso

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

  • Unknown
    El permiso está en un estado desconocido o en iOS, el usuario nunca se le ha pedido.

  • Denied
    El usuario denegó la solicitud del permiso.

  • Disabled
    La característica está deshabilitada en el dispositivo.

  • Granted
    El usuario concedió el permiso o se ha concedido automáticamente.

  • Restricted
    En un estado restringido.

  • Limited
    En un estado limitado. Solo iOS devuelve este estado.

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 sin mostrar ningún cuadro de diálogo al usuario. Los permisos no deben solicitarse desde la clase MauiProgram o App y solo deben solicitarse una vez que haya aparecido la primera página de la aplicación.

En el ejemplo siguiente se solicita el permiso LocationWhenInUse.

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

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

Importante

Ten 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.

Explicación de por qué se necesita el permiso

Es un procedimiento recomendado explicar al usuario 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 en Disabled. 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 informativa. 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.

Ejemplo

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.

public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
    PermissionStatus 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;
}

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 .NET MAUI. Cree una clase que herede de Permissions.BasePermission e implemente los métodos abstractos necesarios. En el código de ejemplo siguiente se muestran los miembros abstractos básicos, pero sin implementación:

public class MyPermission : Permissions.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();
    }

    // Indicates that the requestor should prompt the user as to why the app requires the permission, because the
    // user has previously denied this permission.
    public override bool ShouldShowRationale()
    {
        throw new NotImplementedException();
    }
}

Al implementar un permiso en una plataforma específica, se puede heredar de la clase Permissions.BasePlatformPermission. Esto proporciona métodos auxiliares de la plataforma adicionales para comprobar automáticamente las declaraciones de permisos. Esto ayuda al crearse permisos personalizados que realizan agrupaciones, por ejemplo, solicitando acceso de lectura y escritura al almacenamiento en Android. En el ejemplo de código siguiente se muestra cómo solicitar acceso de almacenamiento de lectura y escritura:

public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
        new List<(string androidPermission, bool isRuntime)>
        {
        (global::Android.Manifest.Permission.ReadExternalStorage, true),
        (global::Android.Manifest.Permission.WriteExternalStorage, true)
        }.ToArray();
}

Después, comprueba el permiso de la misma manera que cualquier otro tipo de permiso proporcionado por .NET MAUI:

PermissionStatus status = await Permissions.RequestAsync<ReadWriteStoragePerms>();

Si quieres llamar a esta API desde el código multiplataforma, puedes crear una interfaz y registrar el permiso personalizado como una dependencia en el contenedor de servicios de la aplicación. En el ejemplo siguiente se muestra la interfaz IReadWritePermission.

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

Después, implementa la interfaz en el permiso personalizado:

public class ReadWriteStoragePermission : 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();
}

En la clase MauiProgram, debes registrar la interfaz y su tipo concreto, y el tipo que consumirá el permiso personalizado, en el contenedor de servicios de la aplicación:

builder.Services.AddTransient<MyViewModel>();
builder.Services.AddSingleton<IReadWritePermission, ReadWriteStoragePermission>();

La implementación de permisos personalizados se puede entonces resolver e invocar desde uno de los tipos, como un modelo de vista:

public class MyViewModel
{
    IReadWritePermission _readWritePermission;

    public MyViewModel(IReadWritePermission readWritePermission)
    {
        _readWritePermission = readWritePermission;
    }

    public async Task CheckPermissionAsync()
    {
        var status = await _readWritePermission.CheckStatusAsync();
        if (status != PermissionStatus.Granted)
        {
            status = await _readWritePermission.RequestAsync();
        }
    }
}

Diferencias entre plataformas

En esta sección se describen las diferencias específicas de la plataforma con la API de permisos.

Los permisos deben tener los atributos coincidentes establecidos en el archivo de manifiesto de Android. El estado del permiso predeterminado es Denied.