NSUserDefaultsDidChangeNotification和Today Extensions

时间:2015-02-02 19:12:46

标签: ios swift nsuserdefaults ios8-today-widget today-extension

我正在使用今日扩展程序开发iPhone应用程序。该应用程序有一个模型模块,可以加载/保存到NSUserDefaults。由于我希望主要应用和扩展程序都可以使用此信息,因此我使用的是应用程序组:

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

应用和扩展程序都可以毫无问题地访问信息。

主应用程序偶尔可能会创建一个本地通知以呈现给用户。该通知有两个与之关联的操作(UIUserNotificationAction)。其中一个操作会触发一些代码在主应用程序的后台运行。该代码更改NSUserDefaults信息并触发同步。我的代码是这样的:

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

现在,在今天外线。我自然会观察对NSUserDefaults上的信息所做的任何更改,以便我可以在窗口小部件上重新加载界面:

override func viewDidLoad() {
    super.viewDidLoad()

    // ...

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

现在,这是我的问题:

  1. 主应用程序安排UILocalNotification。我打开今天的视图,看看今天的小部件。

  2. 当通知触发时,屏幕顶部会显示一条横幅。

  3. 我在那个横幅上向下滑动以显示这两个动作,然后我选择我之前提到的那个动作(今天的小部件仍在现场和屏幕上)。

  4. 我知道该操作在后台正确运行,并且正在对NSUserDefaults上的信息进行更改。

    但是,即使今天小部件一直处于活动状态并且一直显示在屏幕上,也不会触发重新加载操作。经过进一步调查后,我可以确认NSUserDefaultsDidChangeNotification被解雇(我放置了一个断点,它没有触发,也做了一些其他检查)。

    我知道通知操作正在进行更改,因为如果我强制重新加载窗口小部件(通过关闭并打开今天的视图),窗口小部件会正确更新。

    我在网上看过各种教程,他们首先要说的是听取这个通知并更新小部件,以便“小部件与NSUserDefaults同步”。但问题是AFAICT这个通知绝对没用!怎么来的?


    注意1:当我在 今天的小部件中更改NSUserDefaults的信息时,通知会正确触发。

    注意2:调试今天的小部件绝对是可怕的,顺便说一句。在对断点和崩溃做出反应之前,总是需要告诉Xcode“按名称附加到进程...”。 iOS不断为小部件创建一个新的流程,所以我不得不经常告诉Xcode再次附加。

1 个答案:

答案 0 :(得分:12)

来自doc

  

Cocoa包括两种类型的通知中心:   NSNotificationCenter类在单个内部管理通知   处理。 NSDistributedNotificationCenter类管理   在一台计算机上跨多个进程发送通知。

显然,包含应用程序和今天的扩展程序是不同的进程,因为当您今天调试扩展程序时,您希望附加包含应用程序进程,但NSNotificationCenter仅在单个进程中工作。

为了在包含应用和扩展程序之间进行通信,您可以使用 达尔文通知中心 CFNotificationCenter,其工作方式与NSDistributedNotificationCenter类似,仅适用于osx。

这个想法是使用他们共享的组文件夹中的文件。在包含应用程序时,您将要发送的数据写入文件,然后发布CFNotification,将由今天的扩展程序接收。

在今天的扩展中,使用CFNotificationCenterAddObserver来观察CFNotification,一旦收到它,将调用回调,其中由于回调而必须发布NSNotification是C样式函数并且“userInfo”不能被传递在CFNotification中,收到此NSNotification对象后,它开始从文件中读取数据,该文件用于更新通知中心的今天扩展视图。

您可以使用此github code来强制加载今天的扩展视图。它对我有用。
这是一篇很棒的文章。 http://www.atomicbird.com/blog/sharing-with-app-extensions

另一种选择是使用setHasContent函数。当您计划本地标识符时,将content设置为false以隐藏视图,在handleActionWithIdentifier中将其设置为true以显示视图。这样,当您停留在通知中心时,您将暂时看不到该视图,但是当您看到它时,它将是更新的数据。

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

但我认为整个问题是一个罕见的情况,不需要修复,因为您可以获取更新数据重新加载通知中心或切换到通知选项卡并切换回今日标签。