NSUserDefaultsDidChangeNotification et les extensions d'aujourd'hui

Je développe une application iPhone avec une extension Today. L'application dispose d'un module Model chargé de / sauvegardes vers NSUserDefaults. Comme je souhaite que ces informations soient disponibles à la fois pour l'application principale et pour l'extension, j'utilise un groupe d'applications:

let storage = NSUserDefaults(suiteName: "group.etc.etc.etc...") 

L'application et l'extension peuvent accéder à l'information sans problème.

L'application principale peut occasionnellement créer une notification locale à présenter à l'user. Cette notification a deux actions associées (UIUserNotificationAction). Une de ces actions triggers du code exécuté en arrière-plan sur l'application principale. Ce code modifie les informations NSUserDefaults et triggers une synchronisation. Mon code est quelque chose comme ceci:

 func application(application: UIApplication, handleActionWithIdentifier id: Ssortingng?, forLocalNotification not: UILocalNotification, completionHandler: () -> ()) { // Interact with model here // New information gets saved to NSUserDefaults userDefaultsStorage.synchronize() completionHandler() } 

Maintenant, sur le Today Ext. J'observe naturellement toutes les modifications apscopes aux informations sur NSUserDefaults afin que je puisse recharger l'interface sur le widget:

 override func viewDidLoad() { super.viewDidLoad() // ... NSNotificationCenter.defaultCenter().addObserverForName(NSUserDefaultsDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in self.reload() } } 

Maintenant, voici mon problème:

  1. L'application principale planifie un UILocalNotification. J'ouvre la vue d'aujourd'hui et regarde mon widget d'aujourd'hui.

  2. Lorsque la notification se triggers, une bannière apparaît en haut de l'écran.

  3. Je glisse sur cette bannière pour révéler les deux actions et je sélectionne celle que j'ai mentionnée plus tôt (le widget d'aujourd'hui est encore en direct et à l'écran).

Je sais pertinemment que l'action s'exécute correctement en arrière-plan et que les modifications sont apscopes aux informations sur NSUserDefaults.

Cependant, même si le widget d'aujourd'hui a été actif et à l'écran pendant tout ce time, aucune action de rechargement n'est déclenchée. Après un examen plus approfondi, je peux confirmer que le NSUserDefaultsDidChangeNotification n'est pas déclenché (j'ai placé un point d'arrêt et il ne s'est pas déclenché, ainsi que d'autres vérifications).

Je sais que les modifications sont effectuées par l'action de notification car si je force un rechargement du widget (en fermant et en ouvrant la vue d'aujourd'hui) le widget se met à jour correctement.

J'ai vu divers tutoriels en ligne où la première chose qu'ils disent est d'écouter cette notification et de mettre à jour le widget afin que "le widget soit synchronisé avec NSUserDefaults". Mais le truc c'est qu'AFAICT cette notification est absolument inutile! Comment venir??


Remarque 1: lorsque je modifie les informations sur NSUserDefaults à partir du widget Today, la notification se triggers correctement.

Note 2: Déboguer un widget d'aujourd'hui est absolument horrible, btw. Il est toujours nécessaire de dire à Xcode "Attacher au process par son nom …" avant de pouvoir réagir aux points d'arrêt et aux plantages. Et iOS crée constamment un nouveau process pour le widget, je dois donc constamment dire à Xcode de se connecter à nouveau.

De doc ici:

Cocoa comprend deux types de centres de notification: La class NSNotificationCenter gère les notifications au sein d'un même process. La class NSDissortingbutedNotificationCenter gère les notifications sur plusieurs process sur un seul ordinateur.

Apparemment, l'application contenant et l'extension d'aujourd'hui sont des process différents, puisque lorsque vous déboguez l'extension d'aujourd'hui, vous voulez joindre un process contenant une application, mais NSNotificationCenter ne fonctionne que dans un seul process.

Pour communiquer entre l'application et les extensions, vous pouvez utiliser le Centre de notification Darwin CFNotificationCenter qui fonctionne comme NSDissortingbutedNotificationCenter , qui n'est disponible que pour osx.

L'idée est d'utiliser un file dans le dossier du groupe qu'ils partagent. En contenant l'application, vous écrivez datatables que vous voulez envoyer dans le file, puis enregistrez un CFNotification, qui sera reçu par l'extension d'aujourd'hui.

Dans l'extension d'aujourd'hui, utilisez CFNotificationCenterAddObserver pour observer le CFNotification, après réception, callback sera appelé, dans lequel une notification NSNotification doit être affichée en raison de callback est une fonction de style C et "userInfo" ne peut pas être passé dans le CFNotification, après avoir reçu ce Objet NSNotification, il commence à lire datatables du file, qui est utilisé pour mettre à jour l'affichage de l'extension Today dans le centre de notifications.

Vous pouvez utiliser ce code github pour implémenter le chargement de la vue d'extension d'aujourd'hui. Ça marche pour moi.
Voici un excellent article à ce sujet. http://www.atomicbird.com/blog/sharing-with-app-extensions

Une autre option consiste à utiliser la fonction setHasContent. Lorsque vous planifiez un identificateur local, définissez le contenu sur false pour masquer la vue. Dans handleActionWithIdentifier définissez-le sur true pour afficher la vue. De cette façon, lorsque vous restz dans le centre de notification, vous ne verrez pas la vue pendant un moment, mais lorsque vous la verrez, ce seront datatables mises à jour.

 let widgetController = NCWidgetController.widgetController() widgetController.setHasContent(false, forWidgetWithBundleIdentifier: "YourTodayWidgetBundleIdentifier") 

Mais je pense que tout le problème est un cas rare, qui n'a pas besoin d'être résolu car vous pouvez get datatables mises à jour recharger le centre de notification ou passer à l'onglet de notification et revenir à l'onglet Aujourd'hui.