Tout ce que vous devez savoir sur les extensions d'application iOS



Les extensions d'application sont apparues dans iOS 8 et ont rendu le système plus flexible, puissant et abordable pour les utilisateurs. Les applications peuvent être affichées sous forme de widget dans le Centre de notifications, proposer leurs filtres pour les photos dans Photos, afficher un nouveau clavier système et bien plus encore. Dans le même temps, la sécurité des données utilisateur et du système a été préservée. Les fonctionnalités du travail des extensions d'application seront discutées ci-dessous.

Apple a toujours cherché à isoler soigneusement les applications les unes des autres. C'est le meilleur moyen d'assurer la sécurité des utilisateurs et de protéger leurs données. Chaque application se voit attribuer une place distincte dans le système de fichiers avec un accès limité. Les extensions d'application ont permis d'interagir avec l'application sans la lancer ni l'afficher à l'écran. Ainsi, une partie de ses fonctionnalités sera disponible pour les utilisateurs lorsqu'ils interagissent avec d'autres applications ou le système.

Les extensions d'application sont des fichiers exécutables qui s'exécutent indépendamment de l'application conteneur - contenant l'application . En eux-mêmes, ils ne peuvent pas être publiés sur l'App Store, uniquement avec l'application Containing. Toutes les extensions d'application effectuent une tâche spécifique et sont liées à une seule zone d'iOS, selon leur type. Par exemple: les extensions de clavier personnalisées servent à remplacer le clavier standard et les extensions de retouche photo permettent de retoucher des photos dans Photos. Il existe actuellement 25 types d' extensions d'application.

Extension d'application Life Extension


L'application que l'utilisateur utilise pour lancer l'extension d'application est appelée l' application hôte . L'application hôte lance le cycle de vie de l'extension d'application, en lui envoyant une demande en réponse à une action de l'utilisateur:



  • L'utilisateur sélectionne l'extension d'application via l'application hôte.
  • L'application hôte envoie une demande d'extension d'application.
  • iOS lance l'extension d'application dans le contexte de l'application hôte et établit un canal de communication entre eux.
  • L'utilisateur effectue une action dans l'extension d'application.
  • L'extension d'application termine la demande de l'application hôte, effectue une tâche ou démarre un processus d'arrière-plan pour la terminer une fois la tâche terminée, le résultat peut être renvoyé à l'application hôte.
  • Une fois que l'extension d'application exécute son code, le système met fin à cette extension d'application.

Par exemple, lorsque vous partagez une photo à partir de Photos à l'aide de l'extension de partage Facebook, Facebook est l'application contenant et Photos est l'application hôte. Dans ce cas, Photos démarre le cycle de vie de l'extension de partage Facebook lorsque l'utilisateur le sélectionne dans le menu Partager:



Interaction avec l'extension d'application




  • Application contenant - Application hôte
    N'interagissez pas les uns avec les autres.
  • Extension d'application - Application hôte
    Interagissez avec IPC .
  • Extension d'application - contenant une application
    Interaction indirecte. Les groupes d'applications sont utilisés pour l'échange de données et les cadres intégrés sont utilisés pour le code général. Vous pouvez lancer l'application contenant depuis l'extension d'application à l'aide des schémas d'URL .

Code générique: cadres dynamiques


Si l'application contenant l'extension et l'application utilisent le même code, elles doivent être placées dans un cadre dynamique.

Par exemple, une extension de retouche photo peut être associée à une application de retouche photo personnalisée qui utilise certains filtres de l'application contenant. Une bonne solution serait de créer un cadre dynamique pour ces filtres.

Pour ce faire, ajoutez une nouvelle cible et sélectionnez le Cocoa Touch Framework :



Spécifiez un nom (par exemple, ImageFilters ), et dans le panneau du navigateur, vous pouvez voir un nouveau dossier avec le nom du framework créé:

Vous devez vous assurer que le framework n'utilise pas d'API qui ne sont pas disponibles pour les extensions d'application:

  • Partagé depuis UIApplication.
  • API marquées avec des macros d'inaccessibilité.
  • Caméra et microphone (sauf iMessage Extension).
  • Exécution de longues tâches en arrière-plan (les fonctionnalités de cette restriction varient en fonction du type d'extension d'application).
  • Recevez des données à l'aide d'AirDrop.

L'utilisation de l'une de ces listes dans les extensions d'application entraînera son rejet lors de sa publication sur l'App Store.

Dans les paramètres du framework dans Général, vous devez cocher la case à côté de "Autoriser l'API d'extension d'application uniquement" :



Dans le code du framework, toutes les classes, méthodes et propriétés utilisées dans l'application contenant et les extensions d'application doivent être public . Partout où vous devez utiliser le framework, import :

 import ImageFilters 

Échange de données: groupes d'applications


L'application contenant l'extension et l'application ont leurs propres sections limitées du système de fichiers, et seules elles y ont accès. Pour que l'application contenant l'extension et l'application aient un conteneur commun avec un accès en lecture et en écriture, vous devez créer un groupe d'applications pour eux.

Le groupe d'applications est créé dans le portail des développeurs Apple :



Dans le coin supérieur droit, cliquez sur "+", dans la fenêtre qui apparaît, entrez les données nécessaires:



Suivant Continuer -> S'inscrire -> Terminé .

Dans les paramètres de l'application contenant, accédez à l'onglet Capacités , activez les groupes d'applications et sélectionnez le groupe créé:



De même pour l'extension d'application:



Désormais, l'application contenant l'extension et l'application partagent un conteneur. Ensuite, nous parlerons de la lecture et de l'écriture.

UserDefaults


Pour échanger une petite quantité de données, il est pratique d'utiliser UserDefaults , il vous suffit de spécifier le nom du groupe d'applications:

 let sharedDefaults = UserDefaults(suiteName: "group.com.maxial.onemoreapp") 

NSFileCoordinator et NSFilePresenter


Pour les NSFileCoordinator , NSFileCoordinator est mieux adapté pour assurer la cohérence en lecture / écriture. Cela évitera la corruption des données, car il est possible que plusieurs processus puissent y accéder simultanément.

L'URL du conteneur partagé est obtenue comme suit:

 let sharedUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp") 

Record:

 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) } } 

Lecture:

 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) } } 

Il convient de noter que NSFileCoordinator fonctionne de manière synchrone. Alors que certains fichiers seront occupés par un processus, d'autres devront attendre qu'il soit publié.

Si vous voulez que l'extension d'application sache quand l'application contenant change l'état des données, NSFilePresenter utilisé. Il s'agit d'un protocole dont la mise en œuvre peut ressembler à ceci:

 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 propriété presentItemOperationQueue renvoie une file d'attente qui est utilisée pour les rappels lors de la modification des fichiers. La méthode presentedItemDidChange() est appelée lorsqu'un processus, en l'occurrence l'App contenant, modifie le contenu des données. Si des modifications ont été apportées directement à l'aide d'appels d'écriture de bas niveau, le paramètre presentedItemDidChange() pas appelé. Seules les modifications utilisant NSFileCoordinator .

Lors de l'initialisation d'un objet NSFileCoordinator , NSFileCoordinator recommandé de passer l'objet NSFilePresenter , surtout s'il démarre une opération de fichier:

 let fileCoordinator = NSFileCoordinator(filePresenter: self) 

Sinon, l'objet NSFilePresenter recevra des notifications sur ces opérations, ce qui peut entraîner un blocage lors du travail dans le même thread.

Pour commencer à surveiller l'état des données, vous devez appeler la addFilePresenter(_:) avec l'objet correspondant:

 NSFileCoordinator.addFilePresenter(self) 

Tout objet NSFileCoordinator créé NSFileCoordinator automatiquement informé de cet objet NSFilePresenter et notifiera les modifications apportées à son répertoire.

Pour arrêter la surveillance de l'état des données, utilisez removeFilePresenter(_:) :

 NSFileCoordinator.removeFilePresenter(self) 

Données de base


Pour le partage de données, vous pouvez utiliser SQLite et, par conséquent, Core Data. Ils peuvent gérer des processus qui fonctionnent avec des données partagées. Pour configurer les données de base à partager entre l'application contenant et l'extension d'application, créez une sous-classe de NSPersistentContainer et remplacez la méthode defaultDirectoryURL , qui doit renvoyer l'adresse du magasin de données:

 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! } } 

Dans AppDelegate modifiez la propriété persistentContainer . Il est automatiquement créé si, lors de la création d'un projet, cochez la case Utiliser les données de base . Nous allons maintenant retourner un objet de la classe 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 }() 

Il ne reste plus qu'à ajouter .xcdatamodeld à l'extension d'application. Sélectionnez le fichier .xcdatamodeld dans le panneau du navigateur. Dans l' inspecteur de fichiers , sous la section Appartenance cible , cochez la case en regard de l'extension d'application:



Ainsi, l'application contenant l'extension et l'application pourront lire et écrire des données sur le même stockage et utiliser le même modèle.

Lancement de l'application contenant depuis l'extension d'application


Lorsque l'application hôte envoie une demande d'extension d'application, elle fournit un extensionContext . Cet objet a une méthode open(_:completionHandler:) , avec laquelle vous pouvez ouvrir l'application contenant. Cependant, cette méthode n'est pas disponible pour tous les types d'extension d'application. Sur iOS, il est pris en charge par Today Extension et iMessage Extension. L'extension iMessage ne peut l'utiliser que pour ouvrir l'application contenant. Si l'extension Aujourd'hui ouvre une autre application avec elle, une vérification supplémentaire peut être nécessaire pour soumettre à l'App Store.

Pour ouvrir l'application depuis l'extension d'application, vous devez définir le schéma d'URL dans l'application contenant:



Ensuite, appelez la méthode open(_:completionHandler:) avec ce diagramme depuis l'extension d'application:

 guard let url = URL(string: "OneMoreAppUrl://") else { return } extensionContext?.open(url, completionHandler: nil) 

Pour les types d'extensions d'application qui appellent la méthode open(_:completionHandler:) n'est pas disponible, il existe également un moyen. Mais il est possible que l'application soit rejetée lors de la vérification dans l'App Store. L'essence de la méthode consiste à UIResponder la chaîne d'objets UIResponder jusqu'à ce qu'il y ait une application UIApplication qui accepte l'appel 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 } 

Futures extensions d'application


Les extensions d'application ont beaucoup apporté au développement iOS. Progressivement, plus de types d'extensions d'applications apparaissent, leurs capacités se développent. Par exemple, avec la sortie du SDK iOS 12, vous pouvez désormais interagir avec la zone de contenu des notifications, qui manquait depuis si longtemps.

Ainsi, Apple continue de développer cet outil, qui inspire l'optimisme quant à son avenir.

Liens utiles:

Documentation officielle
Partager des données entre des applications iOS et des extensions d'application
Conseils de développement d'extension d'application iOS 8

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


All Articles