Extensiones de iOS en Xamarin.iOS
Creación de extensiones en vídeo de iOS
Las extensiones, como se introdujo en iOS 8, son UIViewControllers
especializadas y son presentadas por iOS dentro de contextos estándar, como dentro del Centro de notificaciones, como tipos de teclado personalizados solicitados por el usuario para realizar entradas especializadas u otros contextos, como editar una foto donde la extensión puede proporcionar filtros de efectos especiales.
Todas las extensiones se instalan junto con una aplicación de contenedor (con ambos elementos escritos mediante las Unified API de 64 bits) y se activan desde un punto de extensión determinado en una aplicación host. Y dado que se utilizarán como complementos de las funciones del sistema existente, deben ser de alto rendimiento, ligeras y sólidas.
Puntos de extensión
Tipo | Descripción | Punto de extensión | Aplicación host |
---|---|---|---|
Action | Editor o visor especializado para un tipo de medio determinado | com.apple.ui-services |
Any |
Proveedor de documentos | Permite que la aplicación use un almacén de documentos remoto | com.apple.fileprovider-ui |
Aplicaciones que usan un UIDocumentPickerViewController |
Teclado | Teclados alternativos | com.apple.keyboard-service |
Any |
Edición de fotos | Manipulación y edición de fotos | com.apple.photo-editing |
Editor Photos.app |
Compartir | Comparte datos con redes sociales, servicios de mensajería, etc. | com.apple.share-services |
Any |
Hoy | Los "widgets" que aparecen en la pantalla Hoy o en el Centro de notificaciones | com.apple.widget-extensions |
Centro de notificaciones y Hoy |
Se agregaron puntos de extensión adicionales en iOS 10 e iOS 12. Puede encontrar la tabla completa de todos los tipos admitidos en la Guía de programación de extensiones de aplicaciones de iOS.
Limitaciones
Las extensiones tienen varias limitaciones, algunas de las cuales son universales para todos los tipos (por ejemplo, ningún tipo de extensión puede acceder a las cámaras o micrófonos), mientras que otros tipos de extensión pueden tener limitaciones específicas en su uso (por ejemplo, los teclados personalizados no se pueden usar para campos de entrada de datos seguros como para contraseñas).
Las limitaciones universales son:
- Los marcos de Kit de mantenimiento y Kit de eventos de interfaz de usuario no están disponibles
- Las extensiones no pueden usar modos de segundo plano extendidos
- Las extensiones no pueden acceder a las cámaras o micrófonos del dispositivo (aunque pueden acceder a los archivos multimedia existentes)
- Las extensiones no pueden recibir datos de Air Drop (aunque pueden transmitir datos a través de Air Drop)
- UIActionSheet y UIAlertView no están disponibles; las extensiones deben usar UIAlertController
- Varios miembros de UIApplication no están disponibles: UIApplication.SharedApplication, UIApplication.OpenUrl, UIApplication.BeginIgnoringInteractionEvents y UIApplication.EndIgnoringInteractionEvents
- iOS aplica un límite de uso de memoria de 16 MB en las extensiones de Hoy.
- De forma predeterminada, las extensiones de teclado no tienen acceso a la red. Esto afecta a la depuración en el dispositivo (la restricción no se aplica en el simulador), ya que Xamarin.iOS requiere acceso a la red para que funcione la depuración. Es posible solicitar acceso a la red estableciendo el valor
Requests Open Access
en Info.plist del proyecto enYes
. Consulte la Guía de teclado personalizado de Apple para obtener más información sobre las limitaciones de la extensión de teclado.
Para conocer las limitaciones individuales, consulte la Guía de programación de extensiones de aplicaciones de Apple.
Distribución, instalación y ejecución de extensiones
Las extensiones se distribuyen desde dentro de una aplicación de contenedor, que, a su vez, se envía y distribuye a través de la App Store. Las extensiones distribuidas con la aplicación se instalan en ese momento, pero el usuario debe habilitar cada extensión explícitamente. Los diferentes tipos de extensiones están habilitados de diferentes maneras; varios requieren que el usuario vaya a la aplicación Configuración y las habilite desde allí. Mientras tanto, otras se activan en el momento del uso, por ejemplo, la extensión Compartir al enviar una foto.
La aplicación en la que se usa la extensión (donde el usuario encuentra el punto de extensión) se conoce como la Aplicación host, ya que es la aplicación que hospeda la extensión cuando se ejecuta. La aplicación que instala la extensión es la aplicación de contenedor, ya que es la aplicación que contenía la extensión cuando se instaló.
Normalmente, la aplicación de contenedor describe la extensión y guía al usuario a través del proceso de habilitación.
Depuración y versiones de lanzamiento de extensiones
Los límites de memoria para las extensiones de aplicación en ejecución son significativamente inferiores a los límites de memoria aplicados a una aplicación en primer plano. Los simuladores que ejecutan iOS tienen menos restricciones aplicadas a las extensiones y puede ejecutar la extensión sin problemas. Sin embargo, ejecutar la misma extensión en un dispositivo puede provocar resultados inesperados, incluido el bloqueo de la extensión o la finalización agresiva del sistema. Por lo tanto, asegúrese de compilar y probar la extensión en un dispositivo antes de enviarlo.
Debe asegurarse de que se aplican las siguientes opciones de configuración al proyecto de contenedor y a todas las extensiones a las que se hace referencia:
- Cree un paquete de aplicación en la configuración de Versión.
- En la configuración del proyecto de Compilación de iOS, establezca la opción Comportamiento del vinculador en Vincular solo SDK del marco o Vincular todo.
- En la configuración del proyecto de Depuración de iOS, desactive las opciones Habilitar depuración y Habilitar generación de perfiles.
Ciclo de vida de la extensión
Una extensión puede ser tan simple como un único UIViewController o extensiones más complejas que presentan varias pantallas de interfaz de usuario. Cuando el usuario encuentra Puntos de extensión (por ejemplo, al compartir una imagen), tendrá la oportunidad de elegir entre las extensiones registradas para ese punto de extensión.
Si elige una de las extensiones de la aplicación, se creará una instancia de su UIViewController
y comenzará el ciclo de vida normal del controlador de vistas. Sin embargo, a diferencia de las aplicaciones normales, que se suspenden pero no se cierran cuando el usuario termina de interactuar con ellas, las extensiones se cargan, se ejecutan y se cierran repetidamente.
Las extensiones pueden comunicarse con sus aplicaciones host a través de un objeto NSExtensionContext. Algunas extensiones tienen operaciones que reciben devoluciones de llamada asincrónicas con los resultados. Estas devoluciones de llamada se ejecutarán en subprocesos en segundo plano y la extensión debe tener esto en cuenta; por ejemplo, mediante NSObject.InvokeOnMainThread si quieren actualizar la interfaz de usuario. Consulte la sección Comunicación con la aplicación host siguiente para obtener más información.
De forma predeterminada, las extensiones y sus aplicaciones de contenedor no se pueden comunicar, a pesar de instalarse juntas. En algunos casos, la aplicación de contenedor es básicamente un contenedor de "envío" vacío cuyo propósito se sirve una vez instalada la extensión. Sin embargo, si las circunstancias lo exigen, la aplicación de contenedor y la extensión pueden compartir recursos desde un área común. Además, una extensión de Hoy puede solicitar su aplicación de contenedor para abrir una dirección URL. Este comportamiento se muestra en el Widget de cuenta regresiva de eventos.
Creación de una extensión
Las extensiones (y sus aplicaciones de contenedor) deben ser archivos binarios de 64 bits y compilarse con las Unified API de Xamarin.iOS. Al desarrollar una extensión, las soluciones contendrán al menos dos proyectos: la aplicación de contenedor y un proyecto para cada extensión que proporciona el contenedor.
Requisitos del proyecto de aplicación de contenedor
La aplicación de contenedor que se usa para instalar la extensión tiene los siguientes requisitos:
- Debe mantener una referencia al proyecto de extensión.
- Debe ser una aplicación completa (debe poder iniciarse y ejecutarse correctamente) incluso si no hace nada más que proporcionar una manera de instalar una extensión.
- Debe tener un identificador de agrupación que sea la base para el identificador de agrupación del proyecto de extensión (consulte la sección siguiente para obtener más detalles).
Requisitos del proyecto de extensión
Además, el proyecto de la extensión tiene los siguientes requisitos:
Debe tener un identificador de agrupación que comience con el identificador de lote de la aplicación de contenedor. Por ejemplo, si la aplicación de contenedor tiene un identificador de agrupación de
com.myCompany.ContainerApp
, el identificador de la extensión podría sercom.myCompany.ContainerApp.MyExtension
:Debe definir la clave
NSExtensionPointIdentifier
, con un valor adecuado (por ejemplocom.apple.widget-extension
, para un widget del Centro de notificaciones de Hoy), en su archivoInfo.plist
.También debe definir o bien la clave
NSExtensionMainStoryboard
o la claveNSExtensionPrincipalClass
en su archivoInfo.plist
con un valor adecuado:- Use la clave
NSExtensionMainStoryboard
para especificar el nombre del Guión gráfico que presenta la interfaz de usuario principal para la extensión (menos.storyboard
). Por ejemplo,Main
para el archivoMain.storyboard
. - Use la clave
NSExtensionPrincipalClass
para especificar la clase que se inicializará cuando se inicie la extensión. El valor debe coincidir con el valor Register delUIViewController
:
- Use la clave
Es posible que determinados tipos de extensiones tengan requisitos adicionales. Por ejemplo, una clase principal de la extensión Hoy o Centro de notificaciones debe implementar INCWidgetProviding.
Importante
Si inicia el proyecto con una de las plantillas de extensiones proporcionadas por Visual Studio para Mac, la mayoría de estos requisitos (si no todos) se proporcionarán y cumplirán automáticamente con la plantilla.
Tutorial
En el siguiente tutorial, creará un widget de Hoy de ejemplo que calcula el día y el número de días restantes en el año:
Creación de la solución
Para crear la solución necesaria, haga lo siguiente:
En primer lugar, cree un proyecto de Aplicación de vista única de iOS y haga clic en el botón Siguiente:
Llame al proyecto
TodayContainer
y haga clic en el botón Siguiente:Compruebe el Nombre del proyecto y SolutionName y haga clic en el botón Crear para crear la solución:
A continuación, en el Explorador de soluciones, haga clic con el botón derecho en la solución y agregue un nuevo Proyecto de extensión de iOS desde la plantilla Extensión de hoy:
Llame al proyecto
DaysRemaining
y haga clic en el botón Siguiente:Revise el proyecto y haga clic en el botón Crear para crearlo:
La solución resultante debe tener ahora dos proyectos, como se muestra aquí:
Creación de la interfaz de usuario de la extensión
A continuación, deberá diseñar la interfaz del widget Hoy. Esto puede hacerse mediante un guión gráfico o creando la interfaz de usuario en el código. Ambos métodos se tratarán a continuación en detalle.
Uso de guiones gráficos
Para compilar la interfaz de usuario con un guión gráfico, haga lo siguiente:
En el Explorador de soluciones, haga doble clic en el archivo
Main.storyboard
del proyecto de extensión para abrirlo y editarlo:Seleccione la etiqueta que se agregó automáticamente a la interfaz de usuario por plantilla y asígnele el nombre
TodayMessage
en la pestaña Widget del Explorador de propiedades:Guarde los cambios en el Storyboard.
Mediante código
Para compilar la interfaz de usuario en el código, haga lo siguiente:
En el Explorador de soluciones, seleccione el proyecto DaysRemaining, agregue una nueva clase y llámela
CodeBasedViewController
:De nuevo, en el Explorador de soluciones, haga doble clic en el archivo
Info.plist
de la extensión para abrirlo y editarlo:Seleccione la Vista de origen (en la parte inferior de la pantalla) y abra el nodo
NSExtension
:Quite la clave
NSExtensionMainStoryboard
y agregue unaNSExtensionPrincipalClass
con el valorCodeBasedViewController
:Guarde los cambios.
A continuación, edite el archivo CodeBasedViewController.cs
y haga que tenga un aspecto similar al siguiente:
using System;
using Foundation;
using UIKit;
using NotificationCenter;
using CoreGraphics;
namespace DaysRemaining
{
[Register("CodeBasedViewController")]
public class CodeBasedViewController : UIViewController, INCWidgetProviding
{
public CodeBasedViewController ()
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Add label to view
var TodayMessage = new UILabel (new CGRect (0, 0, View.Frame.Width, View.Frame.Height)) {
TextAlignment = UITextAlignment.Center
};
View.AddSubview (TodayMessage);
// Insert code to power extension here...
}
}
}
Tenga en cuenta que el [Register("CodeBasedViewController")]
coincide con el valor especificado para el NSExtensionPrincipalClass
anterior.
Codificación de la extensión
Con la interfaz de usuario creada, abra el TodayViewController.cs
o el archivo CodeBasedViewController.cs
(basado en el método usado para crear la interfaz de usuario anterior), cambie el método ViewDidLoad y haga que tenga un aspecto similar al siguiente:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Calculate the values
var dayOfYear = DateTime.Now.DayOfYear;
var leapYearExtra = DateTime.IsLeapYear (DateTime.Now.Year) ? 1 : 0;
var daysRemaining = 365 + leapYearExtra - dayOfYear;
// Display the message
if (daysRemaining == 1) {
TodayMessage.Text = String.Format ("Today is day {0}. There is one day remaining in the year.", dayOfYear);
} else {
TodayMessage.Text = String.Format ("Today is day {0}. There are {1} days remaining in the year.", dayOfYear, daysRemaining);
}
}
Si usa el método de interfaz de usuario basado en código, reemplace el comentario // Insert code to power extension here...
por el nuevo código anterior. Después de llamar a la implementación base (e insertar una etiqueta para la versión basada en código), este código realiza un cálculo sencillo para obtener el día del año y cuántos días quedan. A continuación, muestra el mensaje en la etiqueta (TodayMessage
) que creó en el diseño de la interfaz de usuario.
Tenga en cuenta que este proceso es similar al proceso normal de escritura de una aplicación. Las extensiones UIViewController
tienen el mismo ciclo de vida que un controlador de vistas en una aplicación, excepto que las extensiones no tienen modos en segundo plano y no se suspenden cuando el usuario haya terminado de usarlos. En su lugar, las extensiones se inicializan repetidamente y se desasignan según sea necesario.
Creación de la interfaz de usuario de la aplicación de contenedor
Para este tutorial, la aplicación de contenedor simplemente se usa como método para enviar e instalar la extensión y no proporciona ninguna funcionalidad propia. Edite el archivo Main.storyboard
de TodayContainer y agregue un texto que defina la función de la extensión y cómo instalarlo:
Guarde los cambios en el Storyboard.
Prueba de la extensión
Para probar la extensión en el simulador de iOS, ejecute la aplicación TodayContainer. Se mostrará la vista principal del contenedor:
A continuación, presione el botón Inicio del simulador, desplácese hacia abajo desde la parte superior de la pantalla para abrir el Centro de notificaciones, seleccione la pestaña Hoy y haga clic en el botón Editar:
Agregue la extensión DaysRemaining a la vista Hoy y haga clic en el botón Listo:
El nuevo widget se agregará a la vista Hoy y se mostrarán los resultados:
Comunicación con la aplicación host
El ejemplo de extensión de Hoy que creó anteriormente no se comunica con su aplicación host (la pantalla Hoy). Si lo hiciera, usaría la propiedad ExtensionContext de las clases TodayViewController
o CodeBasedViewController
.
En el caso de las extensiones que recibirán datos de sus aplicaciones host, los datos están en forma de una matriz de objetos NSExtensionItem almacenada en la propiedad InputItemsde ExtensionContextde la extensión UIViewController
.
Otras extensiones, como las extensiones de edición de fotos, pueden distinguir entre el usuario que completa o cancela el uso. Esto se indicará de vuelta a la aplicación host a través de los métodos CompleteRequest y CancelRequest de la propiedad ExtensionContext.
Para obtener más información, consulte Guía de programación de extensiones de aplicaciones de Apple.
Comunicación con la aplicación primaria
Un grupo de aplicaciones permite que aplicaciones diferentes (o una aplicación y sus extensiones) tengan acceso a una ubicación de almacenamiento de archivos compartidos. Los grupos de aplicaciones se pueden usar para datos como:
Para obtener más información, consulte la sección Grupos de aplicaciones de nuestra documentación sobre Cómo trabajar con funcionalidades.
MobileCoreServices
Al trabajar con extensiones, use un Identificador uniforme de tipo (UTI) para crear y manipular datos que se intercambian entre la aplicación, otras aplicaciones o servicios.
La clase estática MobileCoreServices.UTType
define las siguientes propiedades auxiliares relacionadas con las definiciones de kUTType...
de Apple:
kUTTypeAlembic
-Alembic
kUTTypeAliasFile
-AliasFile
kUTTypeAliasRecord
-AliasRecord
kUTTypeAppleICNS
-AppleICNS
kUTTypeAppleProtectedMPEG4Audio
-AppleProtectedMPEG4Audio
kUTTypeAppleProtectedMPEG4Video
-AppleProtectedMPEG4Video
kUTTypeAppleScript
-AppleScript
kUTTypeApplication
-Application
kUTTypeApplicationBundle
-ApplicationBundle
kUTTypeApplicationFile
-ApplicationFile
kUTTypeArchive
-Archive
kUTTypeAssemblyLanguageSource
-AssemblyLanguageSource
kUTTypeAudio
-Audio
kUTTypeAudioInterchangeFileFormat
-AudioInterchangeFileFormat
kUTTypeAudiovisualContent
-AudiovisualContent
kUTTypeAVIMovie
-AVIMovie
kUTTypeBinaryPropertyList
-BinaryPropertyList
kUTTypeBMP
-BMP
kUTTypeBookmark
-Bookmark
kUTTypeBundle
-Bundle
kUTTypeBzip2Archive
-Bzip2Archive
kUTTypeCalendarEvent
-CalendarEvent
kUTTypeCHeader
-CHeader
kUTTypeCommaSeparatedText
-CommaSeparatedText
kUTTypeCompositeContent
-CompositeContent
kUTTypeConformsToKey
-ConformsToKey
kUTTypeContact
-Contact
kUTTypeContent
-Content
kUTTypeCPlusPlusHeader
-CPlusPlusHeader
kUTTypeCPlusPlusSource
-CPlusPlusSource
kUTTypeCSource
-CSource
kUTTypeData
-Database
kUTTypeDelimitedText
-DelimitedText
kUTTypeDescriptionKey
-DescriptionKey
kUTTypeDirectory
-Directory
kUTTypeDiskImage
-DiskImage
kUTTypeElectronicPublication
-ElectronicPublication
kUTTypeEmailMessage
-EmailMessage
kUTTypeExecutable
-Executable
kUTExportedTypeDeclarationsKey
-ExportedTypeDeclarationsKey
kUTTypeFileURL
-FileURL
kUTTypeFlatRTFD
-FlatRTFD
kUTTypeFolder
-Folder
kUTTypeFont
-Font
kUTTypeFramework
-Framework
kUTTypeGIF
-GIF
kUTTypeGNUZipArchive
-GNUZipArchive
kUTTypeHTML
-HTML
kUTTypeICO
-ICO
kUTTypeIconFileKey
-IconFileKey
kUTTypeIdentifierKey
-IdentifierKey
kUTTypeImage
-Image
kUTImportedTypeDeclarationsKey
-ImportedTypeDeclarationsKey
kUTTypeInkText
-InkText
kUTTypeInternetLocation
-InternetLocation
kUTTypeItem
-Item
kUTTypeJavaArchive
-JavaArchive
kUTTypeJavaClass
-JavaClass
kUTTypeJavaScript
-JavaScript
kUTTypeJavaSource
-JavaSource
kUTTypeJPEG
-JPEG
kUTTypeJPEG2000
-JPEG2000
kUTTypeJSON
-JSON
kUTType3dObject
-k3dObject
kUTTypeLivePhoto
-LivePhoto
kUTTypeLog
-Log
kUTTypeM3UPlaylist
-M3UPlaylist
kUTTypeMessage
-Message
kUTTypeMIDIAudio
-MIDIAudio
kUTTypeMountPoint
-MountPoint
kUTTypeMovie
-Movie
kUTTypeMP3
-MP3
kUTTypeMPEG
-MPEG
kUTTypeMPEG2TransportStream
-MPEG2TransportStream
kUTTypeMPEG2Video
-MPEG2Video
kUTTypeMPEG4
-MPEG4
kUTTypeMPEG4Audio
-MPEG4Audio
kUTTypeObjectiveCPlusPlusSource
-ObjectiveCPlusPlusSource
kUTTypeObjectiveCSource
-ObjectiveCSource
kUTTypeOSAScript
-OSAScript
kUTTypeOSAScriptBundle
-OSAScriptBundle
kUTTypePackage
-Package
kUTTypePDF
-PDF
kUTTypePerlScript
-PerlScript
kUTTypePHPScript
-PHPScript
kUTTypePICT
-PICT
kUTTypePKCS12
-PKCS12
kUTTypePlainText
-PlainText
kUTTypePlaylist
-Playlist
kUTTypePluginBundle
-PluginBundle
kUTTypePNG
-PNG
kUTTypePolygon
-Polygon
kUTTypePresentation
-Presentation
kUTTypePropertyList
-PropertyList
kUTTypePythonScript
-PythonScript
kUTTypeQuickLookGenerator
-QuickLookGenerator
kUTTypeQuickTimeImage
-QuickTimeImage
kUTTypeQuickTimeMovie
-QuickTimeMovie
kUTTypeRawImage
-RawImage
kUTTypeReferenceURLKey
-ReferenceURLKey
kUTTypeResolvable
-Resolvable
kUTTypeRTF
-RTF
kUTTypeRTFD
-RTFD
kUTTypeRubyScript
-RubyScript
kUTTypeScalableVectorGraphics
-ScalableVectorGraphics
kUTTypeScript
-Script
kUTTypeShellScript
-ShellScript
kUTTypeSourceCode
-SourceCode
kUTTypeSpotlightImporter
-SpotlightImporter
kUTTypeSpreadsheet
-Spreadsheet
kUTTypeStereolithography
-Stereolithography
kUTTypeSwiftSource
-SwiftSource
kUTTypeSymLink
-SymLink
kUTTypeSystemPreferencesPane
-SystemPreferencesPane
kUTTypeTabSeparatedText
-TabSeparatedText
kUTTagClassFilenameExtension
-TagClassFilenameExtension
kUTTagClassMIMEType
-TagClassMIMEType
kUTTypeTagSpecificationKey
-TagSpecificationKey
kUTTypeText
-Text
kUTType3DContent
-ThreeDContent
kUTTypeTIFF
-TIFF
kUTTypeToDoItem
-ToDoItem
kUTTypeTXNTextAndMultimediaData
-TXNTextAndMultimediaData
kUTTypeUniversalSceneDescription
-UniversalSceneDescription
kUTTypeUnixExecutable
-UnixExecutable
kUTTypeURL
-URL
kUTTypeURLBookmarkData
-URLBookmarkData
kUTTypeUTF16ExternalPlainText
-UTF16ExternalPlainText
kUTTypeUTF16PlainText
-UTF16PlainText
kUTTypeUTF8PlainText
-UTF8PlainText
kUTTypeUTF8TabSeparatedText
-UTF8TabSeparatedText
kUTTypeVCard
-VCard
kUTTypeVersionKey
-VersionKey
kUTTypeVideo
-Video
kUTTypeVolume
-Volume
kUTTypeWaveformAudio
-WaveformAudio
kUTTypeWebArchive
-WebArchive
kUTTypeWindowsExecutable
-WindowsExecutable
kUTTypeX509Certificate
-X509Certificate
kUTTypeXML
-XML
kUTTypeXMLPropertyList
-XMLPropertyList
kUTTypeXPCService
-XPCService
kUTTypeZipArchive
-ZipArchive
Observe el ejemplo siguiente:
using MobileCoreServices;
...
NSItemProvider itemProvider = new NSItemProvider ();
itemProvider.LoadItem(UTType.PropertyList ,null, (item, err) => {
if (err == null) {
NSDictionary results = (NSDictionary )item;
NSString baseURI =
results.ObjectForKey("NSExtensionJavaScriptPreprocessingResultsKey");
}
});
Para obtener más información, consulte la sección Grupos de aplicaciones de nuestra documentación sobre Cómo trabajar con funcionalidades.
Precauciones y consideraciones
Las extensiones tienen mucho menos memoria disponible que las aplicaciones. Se espera que se ejecuten rápidamente y con una intrusión mínima para el usuario y la aplicación en la que se hospedan. Sin embargo, una extensión también debe proporcionar una función distintiva y útil a la aplicación de consumo con una interfaz de usuario de marca que permita al usuario identificar la aplicación de contenedor o desarrollador de la extensión a la que pertenecen.
Dados estos requisitos estrictos, solo debe implementar extensiones que se han probado exhaustivamente y optimizado para el rendimiento y el consumo de memoria.
Resumen
Este documento ha tratado las extensiones, qué son, el tipo de puntos de extensión y las limitaciones conocidas impuestas en una extensión por iOS. Se ha descrito la creación, distribución, instalación y ejecución de extensiones y su ciclo de vida. Se proporcionó un tutorial sobre cómo crear un widget de Hoy sencillo que muestra dos maneras de crear la interfaz de usuario del widget mediante guiones gráficos o código. Se mostró cómo probar una extensión en el simulador de iOS. Por último, se explicó brevemente la comunicación con la aplicación host y algunas precauciones y consideraciones que se deben tomar al desarrollar una extensión.