Pocos lo habían intentado antes ( mira esto ), pero fue Apple con la primera iPhone quién definió cómo un teléfono inteligente y un SO móvil Debería mirar. Apple hizo un avance increíble en hardware y experiencia de usuario. Sin embargo, a menudo olvidamos que también establecen estándares sobre cómo debe funcionar un sistema operativo móvil y cómo deben crearse las aplicaciones de un teléfono inteligente.
La construcción de muros de hormigón entre las aplicaciones, haciéndolas completamente aisladas e inconscientes entre sí, era el mejor método para mantenerlas seguras y proteger sus datos. Todas las actividades fueron monitoreadas de cerca por iOS, y solo hubo un puñado de acciones que una aplicación podría haber realizado fuera de su alcance.
'¡La abstinencia es la mejor protección!' - ¿Pero, dónde está la diversión en eso?
Les tomó un tiempo; demasiado tiempo si me preguntas, pero con iOS 8 Apple decidió divertirse. iOS 8 introdujo un nuevo concepto llamado Extensiones de aplicación. Esta nueva característica no derribó las paredes entre las aplicaciones, pero abrió algunas puertas proporcionando un contacto suave pero tangible entre algunas aplicaciones. La última actualización dio desarrolladores de iOS una opción para personalizar el ecosistema de iOS, y estamos ansiosos por ver que este camino también se abra.
En términos simples, iOS 8 App Extensions proporciona un nuevo método de interactuar con su aplicación, sin iniciarla ni mostrarla en la pantalla.
Como era de esperar, Apple se aseguró de estar al tanto de todo, por lo que solo hay un puñado de nuevos puntos de entrada que su aplicación puede proporcionar:
Las extensiones de aplicación no son aplicaciones independientes. Proporcionan una funcionalidad extendida de la aplicación (a la que se puede acceder desde otras aplicaciones, llamadas aplicaciones de host) que está destinada a ser eficiente y enfocada hacia una sola tarea. Tienen su propio binario, su propia firma de código y su propio conjunto de elementos, pero se entregan a través de la App Store como parte del binario de la aplicación que los contiene. Una aplicación (que la contiene) puede tener más de una extensión. Una vez que el usuario instala una aplicación que tiene extensiones, estarán disponibles en iOS.
Veamos un ejemplo: un usuario encuentra una imagen usando Safari, presiona el botón de compartir y elige la extensión de la aplicación para compartir. Safari 'habla' con el marco social de iOS, que carga y presenta la extensión. El código de la extensión se ejecuta, pasa datos utilizando los canales de comunicación instanciados del sistema y, una vez que se realiza la tarea, Safari elimina la vista de la extensión. Poco después de esto, el sistema finaliza el proceso y su aplicación nunca se muestra en la pantalla. Sin embargo, completó una función para compartir imágenes.
iOS, mediante la comunicación entre procesos, es el responsable de garantizar que la aplicación host y una extensión de la aplicación puedan funcionar juntas. Los desarrolladores utilizan API de alto nivel proporcionadas por el punto de extensión y el sistema, por lo que nunca tienen que preocuparse por los mecanismos de comunicación subyacentes.
Las extensiones de aplicación tienen un ciclo de vida diferente al de las aplicaciones de iOS. La aplicación de host inicia el ciclo de vida de la extensión como respuesta a la acción de un usuario. Luego, el sistema crea una instancia de la extensión de la aplicación y establece un canal de comunicación entre ellos. La vista de la extensión se muestra dentro del contexto de la aplicación de host utilizando los elementos recibidos en la solicitud de la aplicación de host. Una vez que se muestra la vista de la extensión, el usuario puede interactuar con ella. En respuesta a la acción del usuario, la extensión completa la solicitud de la aplicación de host ejecutando / cancelando inmediatamente la tarea o, si es necesario, iniciando un proceso en segundo plano para realizarla. Inmediatamente después de eso, la aplicación de host elimina la vista de la extensión y el usuario vuelve a su contexto anterior dentro de la aplicación de host. Los resultados de realizar este proceso podrían devolverse a la aplicación host una vez que se complete el proceso. Por lo general, la extensión se cancela poco después de completar la solicitud recibida de la aplicación de host (o inicia un proceso en segundo plano para realizarla).
El sistema abre la extensión de la acción de un usuario desde la aplicación de host, la extensión muestra la interfaz de usuario, realiza algún trabajo y devuelve datos a la aplicación de host (si eso es apropiado para el tipo de extensión). La aplicación que la contiene ni siquiera se está ejecutando mientras se ejecuta su extensión.
Las extensiones Today, también llamadas widgets , se encuentran en la vista Hoy del centro de notificaciones. Son una excelente manera de presentar un contenido actualizado para el usuario (como mostrar las condiciones climáticas) o realizar tareas rápidas (como marcar las cosas hechas en el widget de una aplicación de lista de tareas pendientes). Tengo que señalar aquí que el la entrada de teclado no es compatible .
Creemos una extensión de Hoy que mostrará la información más actualizada de nuestra aplicación ( código en GitHub ). Para ejecutar este código, asegúrese de haber (re) configurado el grupo de aplicaciones para el proyecto (seleccione su equipo de desarrollo, tenga en cuenta que el nombre del grupo de aplicaciones debe ser único y siga las instrucciones de Xcode).
Como dijimos antes, las extensiones de aplicación no son aplicaciones independientes. Necesitamos una aplicación contenedora en la que crearemos la extensión de la aplicación. Una vez que tenemos nuestra aplicación contenedora, elegimos agregar un nuevo objetivo navegando a Archivo -> Nuevo -> Objetivo en Xcode. Desde aquí, elegimos la plantilla para nuestro nuevo objetivo para agregar una extensión de hoy.
En el siguiente paso podemos elegir nuestro Nombre de producto. Ese es el nombre que aparecerá en la vista Hoy del Centro de notificaciones. También hay una opción para elegir el idioma entre Swift y Objective-C en este paso. Al finalizar estos pasos, Xcode crea una plantilla Today, que proporciona archivos de implementación y encabezado predeterminados para la clase principal (llamada TodayViewController
) con Info.plist
archivo y un archivo de interfaz (un guión gráfico o un archivo .xib). El Info.plist
archivo, por defecto, se ve así:
Si no desea utilizar el guión gráfico proporcionado por la plantilla, elimine el NSExtension NSExtensionMainStoryboard MainInterface NSExtensionPointIdentifier com.apple.widget-extension
y agregue la tecla NSExtensionMainStoryboard
clave con el nombre de su controlador de vista como valor.
Un widget de Hoy debería:
La extensión de la aplicación y la aplicación que la contiene tienen acceso a los datos compartidos en su contenedor compartido definido de forma privada, que es una forma de comunicación indirecta entre la aplicación que la contiene y la extensión.
¿No te encanta cómo Apple hace que estas cosas sean tan 'simples'? :)
Compartir datos a través de NSExtensionPrincipalClass
es simple y un caso de uso común. De forma predeterminada, la extensión y la aplicación que la contiene utilizan NSUserDefaults
conjuntos de datos y no pueden acceder a los contenedores de los demás. Para cambiar este comportamiento, iOS introdujo Grupos de aplicaciones . Después habilitar grupos de aplicaciones en la aplicación contenedora y la extensión, en lugar de usar NSUserDefaults
utilizar [NSUserDefaults standardUserDefaults]
para acceder al mismo contenedor compartido.
Para garantizar que el contenido esté siempre actualizado, la extensión Today proporciona una API para administrar el estado de un widget y gestionar las actualizaciones de contenido. En ocasiones, el sistema captura instantáneas de la vista del widget, por lo que cuando el widget se vuelve visible, se muestra la instantánea más reciente hasta que se reemplaza con una versión en vivo de la vista. Una conformación al [[NSUserDefaults alloc] initWithSuiteName:@'group.yourAppGroupName']
El protocolo es importante para actualizar el estado de un widget antes de que se tome una instantánea. Una vez que el widget recibe el NCWidgetProviding
call, la vista del widget debe actualizarse con el contenido más reciente y el controlador de finalización debe llamarse con una de las siguientes constantes para describir el resultado de la actualización:
widgetPerformUpdateWithCompletionHandler:
- El nuevo contenido requiere volver a dibujar la vista.NCUpdateResultNewData
- El widget no requiere actualización.NCUpdateResultNoDate
- Ocurrió un error durante el proceso de actualización.Para controlar cuándo se muestra un widget, use el NCUpdateResultFailed
método de - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
clase. Este método le permitirá especificar el estado del contenido del widget. Se puede llamar desde el widget o desde la aplicación que lo contiene (si está activo). Puede pasar un setHasContent:forWidgetWithBundleIdentifier:
o un NCWidgetController
a este método, definiendo que el contenido del widget está listo o no. Si el contenido no está listo, iOS no mostrará su widget cuando se abra la vista Hoy.
El widget Today es la única extensión que puede solicitar la apertura de la aplicación que lo contiene llamando a NO
método. Para asegurarse de que la aplicación contenedora se abra de una manera que tenga sentido en el contexto de la tarea actual del usuario, se debe utilizar un esquema de URL personalizado (que tanto el widget como la aplicación contenedora pueden usar) definido .
Al diseñar su widget, aproveche las ventajas de YES
clase, teniendo en cuenta que las vistas que deben ser borrosas / vibrantes deben agregarse a la NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@'com.your-company.your-app.your-widget'];
y no al openURL:completionHandler:
directamente. Los widgets (que cumplen con el protocolo [self.extensionContext openURL:[NSURL URLWithString:@'customURLsheme://URLpath'] completionHandler:nil];
) deben cargar estados en caché en UIVisualEffectView
para que coincida con el estado de la vista desde el último contentView
y luego pasar sin problemas a los nuevos datos cuando llegan, lo cual no es un caso con un controlador de vista normal (la interfaz de usuario está configurada en UIVisualEffectView
y maneja animaciones y carga de datos en NCWidgetProviding
). Los widgets deben diseñarse para realizar una tarea o abrir la aplicación que los contiene con un solo toque. La entrada del teclado no está disponible dentro de un widget. Esto significa que no se debe utilizar ninguna interfaz de usuario que requiera entrada de texto.
No es posible agregar pergaminos en un widget, tanto vertical como horizontal. O más precisamente, es posible agregar una vista de desplazamiento, pero el desplazamiento no funcionará. El gesto de desplazamiento horizontal en una vista de desplazamiento en la extensión Hoy será interceptado por el centro de notificaciones, lo que provocará el desplazamiento desde Hoy al centro de notificaciones. El desplazamiento vertical de una vista de desplazamiento dentro de una extensión de Hoy se interrumpirá al desplazarse por la Vista de hoy.
A continuación, señalaré algunos aspectos importantes a tener en cuenta al crear una extensión de aplicación.
Los siguientes elementos son válidos para todas las extensiones:
El objeto sharedApplication está fuera de los límites : Las extensiones de la aplicación no pueden acceder a un objeto SharedApplication ni utilizar ninguno de los métodos relacionados con ese objeto.
La cámara y el micrófono están fuera de los límites. : Las extensiones de la aplicación no pueden acceder a la cámara o al micrófono del dispositivo (pero este no es un caso para todos los elementos de hardware). Este es el resultado de la falta de disponibilidad de algunas API. Para acceder a algunos elementos de hardware en la extensión de la aplicación, deberá verificar si su API está disponible para las extensiones de la aplicación o no (con la verificación de disponibilidad de la API descrita anteriormente).
La mayoría de las tareas en segundo plano están fuera de los límites : Las extensiones de aplicación no pueden realizar tareas en segundo plano de larga duración, excepto iniciar cargas o descargas, que se describen a continuación.
AirDrop está fuera de los límites : Las extensiones de la aplicación no pueden recibir (pero pueden enviar) datos usando AirDrop.
La única tarea que se puede realizar en segundo plano es cargar / descargar, usando el viewWillAppear:
.
Una vez iniciada la tarea de carga / descarga, la extensión puede completar la solicitud de la aplicación host y finalizar sin ningún efecto en el resultado de la tarea. Si la extensión no se está ejecutando en el momento en que se completa la tarea en segundo plano, el sistema inicia la aplicación contenedora en segundo plano y el método delegado de la aplicación viewWillDisappear:
se llama.
La aplicación cuya extensión inicia un fondo viewDidLoad
La tarea debe tener un contenedor compartido configurado al que puedan acceder tanto la aplicación contenedora como su extensión.
Asegúrese de crear diferentes sesiones en segundo plano para la aplicación que lo contiene y cada una de sus extensiones de aplicación (cada sesión en segundo plano debe tener un identificador único). Esto es importante porque solo un proceso puede usar una sesión en segundo plano a la vez.
Las diferencias entre las extensiones Action y Share no están completamente claras desde la perspectiva de un codificador, porque en la práctica son muy similares. La plantilla de Xcode para el destino de la extensión para compartir usa viewWillAppear
, que proporciona una interfaz de usuario de vista de redacción estándar que puede usar para compartir en redes sociales, pero no es obligatorio. Una extensión compartida también puede heredar directamente de UIViewController para obtener un diseño completamente personalizado, de la misma manera que una extensión Action puede heredar de NSURLSession object
.
La diferencia entre estos dos tipos de extensiones está en cómo deben usarse. Con la extensión Action, puede crear una extensión sin una interfaz de usuario propia (por ejemplo, una extensión que se usa para traducir el texto seleccionado y devolver la traducción a la aplicación anfitriona). La extensión para compartir le permite compartir comentarios, fotos, videos, audio, enlaces y más directamente desde la aplicación de host. El application:handleEventsForBackgroundURLSession:completionHandler:
impulsa las extensiones de Acción y Compartir, donde las extensiones de Compartir se presentan como íconos de color en la fila superior y las extensiones de acción se presentan como íconos monocromáticos en la fila inferior (Imagen 2.1).
API marcadas en los archivos de encabezado con NSURLSession
No se puede usar una macro, o una macro similar para la indisponibilidad (por ejemplo: los marcos de interfaz de usuario HealthKit y EventKit en iOS 8 no están disponibles para su uso en ninguna extensión de aplicación).
Si está compartiendo código entre una aplicación y una extensión, debe tener en cuenta que incluso hacer referencia a una API que no está permitida para la extensión de la aplicación conducirá al rechazo de su aplicación en la App Store. Puede optar por lidiar con esto volviendo a factorizar las clases compartidas en jerarquías, con un padre común y diferentes subclases para diferentes objetivos. Otra forma es usar el preprocesador mediante SLComposeServiceViewController
cheques. Debido a que todavía no hay un objetivo condicional incorporado, debe crear el suyo propio.
Otra buena forma de hacer esto es creando su propio marco integrado. Solo asegúrese de que no contenga ninguna API que no esté disponible para extensiones. Para configurar una extensión de la aplicación para usar un marco integrado, navegue hasta la configuración de compilación del objetivo y establezca la opción 'Requerir solo API segura para extensión de la aplicación' en Sí. Al configurar el proyecto de Xcode, en la fase de creación de Copiar archivos, se debe elegir “Frameworks” como destino para el framework integrado. Si elige el destino 'SharedFrameworks', su envío será rechazado por App Store.
Aunque las extensiones de aplicación solo han estado disponibles desde iOS 8, puede hacer que su aplicación contenedora esté disponible para las versiones anteriores de iOS.
Tenga en cuenta las pautas de interfaz humana de iOS de Apple cuando diseñar una extensión de aplicación . Debe asegurarse de que su la extensión de la aplicación es universal , independientemente del dispositivo que admita la aplicación que lo contiene. Para asegurarse de que la extensión de la aplicación sea universal, use la configuración de compilación de la familia de dispositivos de destino en Xcode especificando el valor 'iPhone / iPad' (a veces llamado universal).
Las extensiones de aplicaciones definitivamente tienen el impacto más visible en iOS 8. Dado que el 79% de los dispositivos ya usan iOS 8 (según lo medido por la App Store el 13 de abril de 2015), las extensiones de aplicaciones son características increíbles que las aplicaciones deberían aprovechar. Al combinar las restricciones de la API y la forma de compartir datos entre las extensiones y la aplicación que las contiene, parece que Apple logró abordar una de las mayores quejas sobre la plataforma sin comprometer su modelo de seguridad. Todavía no hay forma de que las aplicaciones de terceros compartan directamente sus datos entre sí. Aunque este es un concepto muy nuevo, parece muy prometedor.