
Las extensiones de aplicaciones aparecieron en iOS 8 e hicieron que el sistema fuera más flexible, potente y asequible para los usuarios. Las aplicaciones se pueden mostrar como un widget en el Centro de notificaciones, ofrecer sus filtros para fotos en Fotos, mostrar un nuevo teclado del sistema y mucho más. Al mismo tiempo, se preservó la seguridad de los datos del usuario y del sistema. Las características del trabajo de las extensiones de aplicación se analizarán a continuación.
Apple siempre ha tratado de aislar cuidadosamente las aplicaciones entre sí. Esta es la mejor manera de garantizar la seguridad de los usuarios y proteger sus datos. Cada aplicación tiene un lugar separado en el sistema de archivos con acceso limitado. Las extensiones de aplicación permitieron interactuar con la aplicación sin iniciarla o mostrarla en la pantalla. Por lo tanto, parte de su funcionalidad estará disponible para los usuarios cuando interactúen con otras aplicaciones o el sistema.
Las extensiones de aplicación son archivos ejecutables que se ejecutan independientemente de la aplicación que lo contiene: la aplicación que lo
contiene . Por sí mismos, no se pueden publicar en la App Store, solo con la aplicación que lo contiene. Todas las extensiones de aplicación realizan una tarea específica y están vinculadas a un solo área de iOS, según su tipo. Por ejemplo: las extensiones de teclado personalizadas son para reemplazar el teclado estándar, y las extensiones de edición de fotos son para editar fotos en fotos. Actualmente hay
25 tipos de extensiones
de aplicación.
Extensión de la aplicación Life Extension
La aplicación que utiliza el usuario para iniciar la extensión de la aplicación se denomina
aplicación de host . La aplicación host inicia el ciclo de vida de la extensión de la aplicación y le envía una solicitud en respuesta a una acción del usuario:

- El usuario selecciona la extensión de la aplicación a través de la aplicación host.
- La aplicación host envía una solicitud de extensión de aplicación.
- iOS lanza la extensión de la aplicación en el contexto de la aplicación host y establece un canal de comunicación entre ellas.
- El usuario realiza una acción en la extensión de la aplicación.
- La extensión de la aplicación completa la solicitud de la aplicación host, realiza una tarea o inicia un proceso en segundo plano para completarla; Una vez completada la tarea, el resultado puede devolverse a la aplicación host.
- Una vez que la extensión de la aplicación ejecuta su código, el sistema termina esta extensión de la aplicación.
Por ejemplo, cuando se comparte una foto de Fotos usando la extensión Compartir de Facebook, Facebook es la aplicación que contiene y Fotos es la aplicación de host. En este caso, Fotos inicia el ciclo de vida de la extensión Compartir de Facebook cuando el usuario lo selecciona en el menú Compartir:

Interacción con la extensión de la aplicación

- Aplicación que contiene: aplicación de host
No interactúen entre ellos.
- Extensión de aplicación: aplicación de host
Interactúa con IPC .
- Extensión de la aplicación: que contiene la aplicación
Interacción indirecta. Los grupos de aplicaciones se usan para el intercambio de datos, y Embedded Frameworks se usa para el código general. Puede iniciar la aplicación que contiene desde la extensión de la aplicación utilizando los esquemas de URL .
Código genérico: marcos dinámicos
Si la aplicación que contiene y la extensión de la aplicación usan el mismo código, debe colocarse en un marco dinámico.
Por ejemplo, una extensión de edición de fotos puede estar asociada con una aplicación de edición de fotos personalizada que utiliza algunos filtros de la aplicación que contiene. Una buena solución sería crear un marco dinámico para estos filtros.
Para hacer esto, agregue un nuevo
Target y seleccione
Cocoa Touch Framework :

Especifique un nombre (por ejemplo,
ImageFilters ), y en el panel del navegador puede ver una nueva carpeta con el nombre del marco creado:

Debe asegurarse de que el marco no utiliza API que no están disponibles para las extensiones de aplicación:
- Compartido desde la aplicación UIA.
- API marcadas con macros de inaccesibilidad.
- Cámara y micrófono (excepto la extensión iMessage).
- Realización de largas tareas en segundo plano (las características de esta restricción varían según el tipo de extensión de la aplicación).
- Reciba datos utilizando AirDrop.
El uso de cualquiera de esta lista en las extensiones de la aplicación provocará su rechazo cuando se publique en la App Store.
En la configuración del marco en
General, debe marcar la casilla junto a
"Permitir solo API de extensión de aplicación" :

En el código marco, todas las clases, métodos y propiedades utilizados en la aplicación que contiene y las extensiones de la aplicación deben ser
public
. Donde sea que necesite usar el marco,
import
:
import ImageFilters
Intercambio de datos: grupos de aplicaciones
La aplicación que contiene y la extensión de la aplicación tienen sus propias secciones limitadas del sistema de archivos, y solo ellas tienen acceso a ellas. Para que la aplicación que contiene y la extensión de la aplicación tengan un contenedor común con acceso de lectura y escritura, debe crear un grupo de aplicaciones para ellos.
App Group se crea en el
Portal de desarrolladores de Apple :

En la esquina superior derecha, haga clic en "+", en la ventana que aparece, ingrese los datos necesarios:

Siguiente
Continuar -> Registrarse -> Listo .
En la Configuración de la aplicación que contiene, vaya a la pestaña
Capacidades , active Grupos de aplicaciones y seleccione el grupo creado:

De manera similar para la extensión de la aplicación:

Ahora la aplicación que contiene y la extensión de la aplicación comparten un contenedor. A continuación, hablaremos sobre cómo leerlo y escribirle.
Valores predeterminados del usuario
Para intercambiar una pequeña cantidad de datos, es conveniente usar
UserDefaults
, solo necesita especificar el nombre del grupo de aplicaciones:
let sharedDefaults = UserDefaults(suiteName: "group.com.maxial.onemoreapp")
NSFileCoordinator y NSFilePresenter
Para big data,
NSFileCoordinator
es más adecuado para garantizar la coherencia de lectura / escritura. Esto evitará la corrupción de datos, ya que existe la posibilidad de que varios procesos puedan acceder a ellos simultáneamente.
La URL del contenedor compartido se obtiene de la siguiente manera:
let sharedUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp")
Registro:
fileCoordinator.coordinate(writingItemAt: sharedUrl, options: [], error: nil) { [unowned self] newUrl in do { let data = try NSKeyedArchiver.archivedData(withRootObject: self.object, requiringSecureCoding: false) try data.write(to: newUrl, options: .atomic) } catch { print(error) } }
Lectura:
fileCoordinator.coordinate(readingItemAt: sharedUrl, options: [], error: nil) { newUrl in do { let data = try Data(contentsOf: newUrl) if let object = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSString.self, from: data) as String? { self.object = object } } catch { print(error) } }
Vale la pena considerar que
NSFileCoordinator
funciona sincrónicamente. Si bien algunos archivos estarán ocupados por algún proceso, otros tendrán que esperar a que se publique.
Si desea que la extensión de la aplicación sepa cuándo la aplicación que contiene cambia el estado de los datos,
NSFilePresenter
utiliza
NSFilePresenter
. Este es un protocolo cuya implementación puede verse así:
extension TodayViewController: NSFilePresenter { var presentedItemURL: URL? { let sharedUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp") return sharedUrl?.appendingPathComponent("Items") } var presentedItemOperationQueue: OperationQueue { return .main } func presentedItemDidChange() { } }
La propiedad
presentedItemOperationQueue
devuelve una cola que se utiliza para devoluciones de llamada al cambiar archivos. El método
presentedItemDidChange()
se llama cuando un proceso, en este caso la aplicación que contiene, cambia el contenido de los datos. Si se han realizado cambios directamente mediante llamadas de escritura de bajo nivel, no se llama a
presentedItemDidChange()
. Solo
NSFileCoordinator
cambios que utilizan
NSFileCoordinator
.
Al inicializar un objeto
NSFileCoordinator
,
NSFileCoordinator
recomienda que pase el objeto
NSFilePresenter
, especialmente si inicia cualquier operación de archivo:
let fileCoordinator = NSFileCoordinator(filePresenter: self)
De lo contrario, el objeto
NSFilePresenter
recibirá notificaciones sobre estas operaciones, lo que puede llevar a un punto muerto cuando se trabaja en el mismo hilo.
Para comenzar a monitorear el estado de los datos, debe llamar al
addFilePresenter(_:)
con el objeto correspondiente:
NSFileCoordinator.addFilePresenter(self)
Cualquier objeto
NSFileCoordinator
creado posteriormente
NSFileCoordinator
automáticamente sobre este objeto
NSFilePresenter
y notificará sobre los cambios en su directorio.
Para dejar de monitorear el estado de los datos, use
removeFilePresenter(_:)
:
NSFileCoordinator.removeFilePresenter(self)
Datos centrales
Para compartir datos, puede usar SQLite y, en consecuencia, Core Data. Pueden administrar procesos que funcionan con datos compartidos. Para configurar los datos principales que se compartirán entre la aplicación que contiene y la extensión de la aplicación, cree una subclase de
NSPersistentContainer
y anule el método
defaultDirectoryURL
, que debería devolver la dirección del almacén de datos:
class SharedPersistentContainer: NSPersistentContainer { override open class func defaultDirectoryURL() -> URL { var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp") storeURL = storeURL?.appendingPathComponent("OneMoreApp.sqlite") return storeURL! } }
En
AppDelegate
cambie la propiedad
persistentContainer
. Se crea automáticamente si, al crear un proyecto, marque la casilla de verificación
Usar datos principales . Ahora devolveremos un objeto de la clase
SharedPersistentContainer
:
lazy var persistentContainer: NSPersistentContainer = { let container = SharedPersistentContainer(name: "OneMoreApp") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }()
Todo lo que queda es agregar
.xcdatamodeld a la extensión de la aplicación. Seleccione el archivo .xcdatamodeld en el panel del navegador. En el
Inspector de archivos , en la sección
Membresía de destino , marque la casilla junto a Extensión de la aplicación:

Por lo tanto, la aplicación que contiene y la extensión de la aplicación podrán leer y escribir datos en el mismo almacenamiento y usar el mismo modelo.
Inicio de la aplicación que contiene desde la extensión de la aplicación
Cuando la aplicación de host envía una solicitud de extensión de aplicación, proporciona un
extensionContext
. Este objeto tiene un método
open(_:completionHandler:)
, con el que puede abrir la aplicación que contiene. Sin embargo, este método no está disponible para todos los tipos de extensión de aplicación. En iOS, es compatible con Today Extension y iMessage Extension. iMessage Extension solo puede usarlo para abrir la aplicación que contiene. Si Today Extension abre otra aplicación con ella, es posible que se requiera una verificación adicional para enviarla a la App Store.
Para abrir la aplicación desde la extensión de la aplicación, debe definir el esquema de URL en la aplicación que lo contiene:

A continuación, llame al método
open(_:completionHandler:)
con este diagrama desde la extensión de la aplicación:
guard let url = URL(string: "OneMoreAppUrl://") else { return } extensionContext?.open(url, completionHandler: nil)
Para esos tipos de extensiones de aplicación que llaman al método
open(_:completionHandler:)
no está disponible, también hay una manera. Pero existe la posibilidad de que la aplicación sea rechazada al registrarse en la App Store. La esencia del método es atravesar la cadena de objetos
UIResponder
hasta que haya una aplicación
UIApplication
que acepte la llamada
openURL
:
guard let url = URL(string: "OneMoreAppUrl://") else { return } let selectorOpenURL = sel_registerName("openURL:") var responder: UIResponder? = self while responder != nil { if responder?.responds(to: selectorOpenURL) == true { responder?.perform(selectorOpenURL, with: url) } responder = responder?.next }
Extensiones de aplicaciones futuras
Las extensiones de aplicación han aportado mucho al desarrollo de iOS. Gradualmente, aparecen más tipos de extensiones de aplicaciones, sus capacidades se están desarrollando. Por ejemplo, con el lanzamiento de iOS 12 SDK, ahora puede interactuar con el área de contenido en las notificaciones, que ha estado ausente durante tanto tiempo.
Por lo tanto, Apple continúa desarrollando esta herramienta, que inspira optimismo sobre su futuro.
Enlaces utiles:Documentación oficialCompartir datos entre aplicaciones iOS y extensiones de aplicacionesConsejos de desarrollo de la extensión de la aplicación iOS 8