
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 officiellePartager des données entre des applications iOS et des extensions d'applicationConseils de développement d'extension d'application iOS 8