Todo lo que necesitas saber sobre las extensiones de aplicaciones iOS



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 oficial
Compartir datos entre aplicaciones iOS y extensiones de aplicaciones
Consejos de desarrollo de la extensión de la aplicación iOS 8

Source: https://habr.com/ru/post/441890/


All Articles