Communication et conservation des données entre les applications avec des groupes d'applications

85

iOS 8 a révélé hier une nouvelle API concernant les groupes d'applications. C'était un peu compliqué avant de partager des données et de communiquer entre les applications et je pense que c'est précisément ce que les groupes d'applications sont censés corriger.

Dans mon application, j'ai activé les groupes d'applications et ajouté un nouveau groupe, mais je ne trouve pas de documentation sur la façon de l'utiliser. La documentation et les références API indiquent uniquement comment ajouter un groupe.

Alors, à quoi servent vraiment les groupes d'applications? Y a-t-il une documentation quelque part sur la façon de l'utiliser?

streem
la source

Réponses:

81

Un autre avantage des groupes d'applications est la possibilité de partager une NSUserDefaultsbase de données. Cela fonctionne également pour les extensions d'application (widgets du centre de notification, claviers personnalisés, etc.).

Initialisez votre NSUserDefaultsobjet comme ceci dans toutes les applications du groupe d'applications et elles partageront la base de données:

Objectif c:

[[NSUserDefaults alloc] initWithSuiteName:@"<group identifier>"];

Rapide:

NSUserDefaults(suiteName: "<group identifier>")

Gardez à l'esprit que tout ce qui se trouve dans la [NSUserDefaults standardUserDefaults]base de données de chaque application ne sera pas transféré dans cette base de données.

La documentation donne également un exemple correct (à partir de la version bêta 3).

Et n'oubliez pas de synchroniser la base de données:

[yourDefaults synchronize];
père Noël
la source
2
Fonctionne uniquement sur simulateur (XCode 6 beta 5, iOS 8 beta 5)
iOS Dev
8
REMARQUE: Si vous l'utilisez avec une extension iOS 8 (par exemple un clavier), vous devrez vous assurer que votre extension a RequestsOpenAccess = YES dans son fichier Info.plist.
Kiran Panesar
1
J'ai fait RequestsOpenAccess = YES et suivez les instructions, cela ne fonctionnera toujours pas pour moi sur l'appareil et fonctionne bien sur l'émulateur, quelque chose de spécifique pour l'appareil?
MAC
Même problème avec moi, j'ai intégré l'extension de partage dans mon application, cela fonctionne sur simulateur, mais ne fonctionne pas sur un appareil réel, j'ai ajouté RequestOpenAccess = YES mais cela ne fonctionne pas, ma question est stackoverflow.com/questions/30489894/ …
Ravi Ojha
3
@MikeRichards pas sûr, mais si je me souviens bien, les groupes d'applications sont préfixés par un identifiant d'équipe
Père Noël
74

Partage de données NSUserDefaults entre plusieurs applications

Pour partager les valeurs par défaut entre une application et une extension ou entre 2 applications, vous devez ajouter un groupe d'applications dans vos paramètres en suivant les étapes suivantes:

  1. Dans le navigateur de projet, cliquez sur le fichier * .xcodeproj (devrait être en haut).
  2. À droite du navigateur de projet, recherchez Projet et cibles. Sous les cibles, cliquez sur votre cible principale (devrait être la première chose sous les cibles).
  3. Vers le haut, cliquez sur l'onglet Capacités.
  4. Dans la section Groupes d'applications, cliquez sur le commutateur à droite pour activer les groupes d'applications.
  5. Cliquez sur le bouton + et ajoutez un groupe d'applications nommé group.com.company.myApp .
  6. Accédez au même endroit dans vos autres applications et ce groupe devrait maintenant être disponible à la sélection. Activez ce groupe pour chaque application qui utilisera ces données partagées.

Remarque: si vous accédez au portail des développeurs Apple (le site Web Apple qui affiche tous vos certificats, identifiants, appareils et profils d'approvisionnement) et accédez à Identificateurs> Groupes d'applications, vous devriez voir ce nouveau groupe d'applications.

Pour stocker des données:

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")!
userDefaults.setObject("user12345", forKey: "userId")
userDefaults.synchronize()

Pour récupérer des données:

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
if let testUserId = userDefaults?.objectForKey("userId") as? String {
  print("User Id: \(testUserId)")
}
TenaciousJay
la source
2
Merci! Il a fallu un peu de recherche dans la documentation pour comprendre comment faire cela et j'ai pensé que d'autres pourraient apprécier les mesures que j'ai prises.
TenaciousJay
Merci pour la description détaillée ...! avons-nous besoin de créer un certificat à partir du portail des développeurs Apple (le site Web Apple qui affiche tous vos certificats, identifiants, appareils et profils d'approvisionnement) pour activer ce groupe? même comme la notification push.?
Arbaz Shaikh
Lorsque vous effectuez l'étape 5, il devrait ajouter le groupe d'applications au portail des développeurs Apple, vous ne devriez pas avoir à accéder directement au portail des développeurs pour l'ajouter. Après avoir effectué l'étape 5, vous pouvez accéder au portail et voir qu'il aurait dû le créer pour vous.
TenaciousJay
@TenaciousJay Superbe réponse. Je veux juste ajouter Si vous voulez lancer l'application depuis l'application func (app: UIApplication, url openURL: NSURL, options: [String: AnyObject]) -> Bool
Taimur Ajmal
très bonne réponse. merci @TenaciousJay. J'essaie de récupérer une variable et sa valeur d'AppB dans AppA, comment puis-je faire cela? Par exemple, si "riderCancelledRequest = true" dans AppB, comment puis-je récupérer cela dans AppA?
LizG
37

Les groupes d'applications, selon mon interprétation de la documentation existante, sont principalement ciblés pour les extensions, plus précisément pour les widgets. Les widgets sont leur propre ensemble d'applications qui coexistent avec votre application. Comme il s'agit d'une application distincte et qu'il possède donc son propre bac à sable, vous devrez utiliser des groupes d'applications pour partager des fichiers.

Après quelques greffes d'en-tête, je pense que j'ai trouvé l'API nécessaire, mais elle a en fait été intégrée dans iOS 7.

NSFileManagera une méthode sur containerURLForSecurityApplicationGroupIdentifier:laquelle vous pouvez transmettre l'identifiant que vous avez créé lors de l'activation des groupes d'applications pour vos applications:

NSURL *containerURL = [[NSFileManager defaultManager] 
           containerURLForSecurityApplicationGroupIdentifier:@"group.com.company.app"];
Wayne Hartman
la source
Merci, je crois que vous avez raison sur l'extensibilité. Je suis un peu plus dubitatif sur cette méthode iOS 7, cela me semble assez statique, iOS 8 a introduit de vraies interactions et une communication entre les applications.
streem
@Justafinger Cette méthode iOS 7 sert uniquement à créer une URL pour écrire et lire à partir des données entre les applications partagées. Cette méthode particulière n'est vraiment utile (d'après ce que je vois jusqu'à présent) que pour les widgets, mais pas pour les autres extensions.
Wayne Hartman
Eh bien, je pense qu'Apple voulait rendre cela plus facile dans iOS 8 sans avoir à créer un conteneur par programmation et à partager des fichiers personnalisés. La documentation indique que vous devriez pouvoir partager des données à l'aide de NSUserDefault. Je suppose que quelque chose me manque, car cela ne fonctionne pas pour moi.
streem
@ Justafinger Je n'ai pas pu non plus me rendre NSUserDefaultsau travail. Je craie jusqu'à bêta 1 bug.
Wayne Hartman
@WayneHartman Il existe une solution de contournement pour la NSUserDefaultsbase de données. Voyez ma réponse.
Santa Claus
6

Un piège important dans lequel j'ai puisé aujourd'hui est le suivant:

Dans de nombreux projets, j'ai vu une seule cible d'application et différents identifiants de bundle définis pour chaque configuration de cette cible. Ici, les choses se compliquent. L'objectif des développeurs était de créer une application de débogage pour la configuration de débogage et une application de production pour la cible de publication.

Si vous le faites, les deux applications partageront les mêmes NSUserDefaults lorsqu'elles sont configurées comme suit

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
userDefaults!.setObject("user12345", forKey: "userId")
userDefaults!.synchronize()

Cela pose des problèmes dans de nombreux endroits:

  1. Imaginez que vous définissiez OUI pour une clé lorsqu'un écran d'introduction d'application spécial a été montré à l'utilisateur. L'autre application lira désormais également OUI et ne montrera pas l'intro.
  2. Oui, certaines applications stockent également les jetons oAuth dans leurs paramètres par défaut. Quoi qu'il en soit ... En fonction de l'implémentation, l'application reconnaîtra qu'il y a un jeton et commencera à récupérer des données en utilisant le mauvais jeton. Il y a de fortes chances que cela échoue avec d'étranges erreurs.

La solution à ce problème en général est de préfixer les clés par défaut avec la configuration actuelle construite. Vous pouvez détecter facilement la configuration au moment de l'exécution en définissant différents identificateurs de bundle pour vos configurations. Ensuite, lisez simplement l'identifiant du bundle à partir de NSBundle.mainBundle(). Si vous avez les mêmes identifiants de bundle, vous devez définir différentes macros de préprocesseur comme

#ifdef DEBUG
  NSString* configuration = @"debug";
#elif RELEASE
  NSString* configuration = @"release";
#endif

Dans Swift, ce sera presque le même:

#if DEBUG
  let configuration = "debug"
#elseif RELEASE
  let configuration = "release"
#endif
blackjacx
la source
8
Vos problèmes existent parce que vous mélangez toutes vos données dans un UserDefaults partagé. Vous devez utiliser NSUserDefaults.standardUserDefaults()pour les données qui ne doivent pas être partagées.
Daniel
2

Ranger

let shared: NSUserDefaults = NSUserDefaults(suiteName: "group.abcapp")!
shared.setObject("abc.png", forKey: "favEmoji")
shared.synchronize()
Hardik Thakkar
la source