Cómo Construir Widgets de iOS con .NET MAUI

Cómo Construir Widgets de iOS con .NET MAUI

Este artículo, escrito por Toine de Boer, un desarrollador .NET especializado en .NET MAUI y servicios backend ASP.NET, aborda la creación de Widgets completos y profesionales para iOS utilizando .NET MAUI. Surge de la experiencia del autor con la escasa documentación y los obstáculos encontrados en el desarrollo inicial de widgets. El objetivo es demostrar que es totalmente posible construir widgets de manera comparable al desarrollo nativo, garantizando estabilidad.

Aunque no es un tutorial paso a paso, el texto cubre los aspectos más importantes y los mayores desafíos en orden lógico. Se recomienda tener experiencia con .NET MAUI o Xamarin y, crucialmente, acceso a macOS. El contenido avanza desde la creación de un widget estático simple hasta un sistema para un widget completamente interactivo. Para facilitar el inicio, Toine ha proporcionado un ejemplo funcional en GitHub.

Es importante recordar que los Widgets de iOS son aplicaciones autónomas que se vinculan a una aplicación anfitriona (la aplicación .NET MAUI).

Requisitos Previos Esenciales

Antes de comenzar, se necesitan configuraciones específicas en la consola de desarrollador de Apple. Además del Bundle ID de la aplicación MAUI existente (ej. com.enbyin.WidgetExample), se requiere un Bundle ID separado para el Widget (ej. com.enbyin.WidgetExample.WidgetExtension). Ambos IDs deben tener habilitada la capacidad App Groups con un Group ID dedicado, que convencionalmente se forma prefijando el Bundle ID de la aplicación con group (ej. group.enbyin.WidgetExample).

Creación del Proyecto del Widget

El proceso comienza en Xcode, un paso que puede ser desafiante para desarrolladores .NET. Se recomienda usar VS Code junto con Copilot para agilizar la iteración. Los pasos iniciales son:

  1. Crear un proyecto de aplicación base en Xcode: Utiliza la plantilla ‘App’ con Swift. Asígnale el mismo Bundle ID que tu aplicación .NET MAUI. Este proyecto no se distribuye, solo sirve como base y para pruebas ligeras.
  2. Añadir la extensión del Widget: En Xcode, ve a ‘File > New > Target’ y selecciona la plantilla ‘Widget Extension’. Nómbralo con el Bundle ID correcto para el widget desde el principio. Selecciona ‘Include Configuration App Intent’ para generar datos de ejemplo y tener un widget funcional inmediatamente.
  3. Alinear versiones de iOS: Asegúrate de que la versión mínima de iOS sea la misma para todos los destinos de Xcode en la pestaña ‘General’ de la configuración de la solución.
  4. Realiza una prueba inicial en un dispositivo.

Objetos y Flujos del Widget

El código generado por Xcode puede ser abrumador. El autor aconseja refactorizarlo inmediatamente, moviendo cada objeto a su propio archivo y creando una estructura de carpetas, ya que Swift no utiliza realmente espacios de nombres. Los objetos clave y sus roles son:

  • WidgetBundle: Punto de entrada, expone uno o más widgets.
  • Widget: Configuración específica del widget (View, Provider, ConfigurationIntent, tamaños).
  • AppIntentTimelineProvider: Provee modelos de datos para la vista a través de una línea de tiempo (placeholder para carga, snapshot para galería/adición inicial, timeline para uso normal).
  • TimelineEntry: Instancia del modelo de datos.
  • View: Elementos visuales del widget.
  • WidgetConfigurationIntent: Permite al usuario configurar el widget.

Es fundamental entender que un Widget de iOS es un objeto estático de vida muy corta, con funciones ejecutándose como procesos distintos. Por lo tanto, el manejo de datos en memoria no es efectivo; se debe utilizar alguna forma de almacenamiento local para el intercambio y persistencia de datos.

Iconos de la Aplicación (App Icons)

Para evitar problemas con iconos incorrectos, se deben añadir explícitamente las imágenes AppIcon a los Assets de la extensión del Widget y referenciarlas en su Info.plist. Si los problemas persisten, un reinicio del dispositivo de prueba puede ser necesario. Se puede usar un generador de iconos online para obtener todos los formatos. Las configuraciones de Info.plist relevantes incluyen NSExtensionPrincipalClass, CFBundleIcons y CFBundleIconName, ajustando NSExtensionPrincipalClass con el formato {YourWidgetModuleName}.{YourWidgetName}.

Creación de la Compilación de Lanzamiento del Widget

Para crear builds de lanzamiento del widget (archivos .appex), el autor utiliza un script estándar de xcodebuild que genera los archivos para iphoneos e iphonesimulator y los deposita en una carpeta XReleases. Esto simplifica el proceso y es útil para pipelines de construcción.

Adición del Lanzamiento del Widget a la Aplicación MAUI

Para que la aplicación .NET MAUI incluya el widget, los archivos .appex generados deben colocarse bajo Platforms/iOS/ y configurarse en el archivo .csproj de la aplicación MAUI. Esto se hace mediante un ItemGroup que usa la condición $(TargetFramework.Contains('-ios')) e incluye:

  • Sentencias <Content> con CopyToOutputDirectory="PreserveNewest" para los archivos .appex específicos de Release-iphoneos y Release-iphonesimulator.
  • Un <AdditionalAppExtensions> que especifica la ruta de la carpeta de la extensión, su <Name> y el <BuildOutput> para cada plataforma.

En este punto, el widget debería ser visible en la compilación de la aplicación .NET MAUI, funcionando de forma independiente sin comunicación aún con la aplicación principal. Es importante destacar que las extensiones de widget probablemente no serán visibles al compilar desde Visual Studio para ‘iOS Local Devices’.

Compartir Datos entre la Aplicación y el Widget

Dado que la aplicación .NET MAUI y el Widget son entidades separadas, no pueden intercambiar datos directamente en memoria. Para compartir información, se utiliza el mecanismo de UserDefaults en iOS, al cual se mapean las Preferencias de .NET MAUI. Para que ambos proyectos accedan al mismo origen de datos, es crucial que ambos utilicen un archivo Entitlements.plist que especifique el mismo Group ID configurado previamente. Este archivo Entitlements.plist debe ser incluido y referenciado en el .csproj de la aplicación MAUI bajo el elemento <AdditionalAppExtensions>.

En el código .NET MAUI, no se debe usar Preferences.Default, sino proporcionar explícitamente el Group ID en el parámetro sharedName (ej. Preferences.Set("MyDataKey", "my data to share", "group.com.enbyin.WidgetExample")). En Swift, se usa UserDefaults(suiteName: "group.com.enbyin.WidgetExample")?.set(...). Las claves de almacenamiento son sensibles a mayúsculas y minúsculas.

Comunicación de la Aplicación al Widget

Para notificar al Widget que hay nuevos datos disponibles desde la aplicación, se utiliza la API WidgetKit de Apple. Dado que esta API no está directamente disponible en .NET MAUI, se necesita un binding; el autor sugiere usar el paquete NuGet WidgetKit.WidgetCenterProxy. La API permite recargar todos los widgets del dispositivo o, preferentemente, solo los widgets de un kind específico (ej. widgetCenterProxy.ReloadTimeLinesOfKind("MyWidget")). Es importante usar esta función con moderación, ya que el sistema operativo puede ignorar solicitudes demasiado frecuentes.

Comunicación del Widget a la Aplicación

La comunicación inversa, del Widget a la aplicación, se puede realizar de dos maneras:

  1. Deep Links con widgetUrl(): Al tocar el widget, se abre la aplicación con un Deep Link que puede incluir datos en la URL. Sin embargo, la URL debe determinarse de antemano al configurar la vista del widget.
  2. AppIntents para Widgets Interactivos: Los AppIntents permiten adjuntar acciones/lógica a elementos interactivos (como botones) dentro del widget. Son útiles para ejecutar acciones más largas, como llamadas HTTP, cambiar un valor en el almacenamiento compartido y luego recargar el widget para reflejar el cambio, creando así «Widgets Interactivos».

Un punto clave es que un Widget de iOS no puede comunicarse con la aplicación en segundo plano. Cualquier llamada directa la trae al primer plano. Para realizar trabajo sin abrir la aplicación, se pueden usar AppIntents en el Widget para llamar a un backend, que a su vez envía una notificación push silenciosa a la aplicación, permitiendo que esta última maneje la actualización en segundo plano.

Optimización del Desarrollo de Widgets

Una vez que el widget interactivo está implementado, el siguiente paso es refinar la lógica, el diseño y el estilo. Idealmente, la mayor parte de la lógica reside en la aplicación .NET MAUI para su reutilización. Sin embargo, ciertas partes en Swift son inevitables (manejo de almacenamiento, construcción de vistas, pequeñas comunicaciones con el backend). Para facilitar la transición de C# a Swift, Toine recomienda:

  • Utilizar VS Code y Copilot para programar en Swift, aprovechando su asistencia como compañero de programación.
  • Mantener Xcode abierto para ciclos rápidos de compilación y prueba, capturando problemas a tiempo.
  • Abrir la vista Canvas en Xcode y usar datos #preview para ver los cambios visuales al instante después de cada compilación.

El artículo concluye reiterando estos consejos prácticos y anuncia la próxima publicación de un artículo sobre cómo construir Widgets de Android con .NET MAUI, invitando a los lectores a estar atentos.

Author: Enagora

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *