NSUserDefaultsDidChangeNotification和Today Extensions

Ale*_*lex 15 nsuserdefaults ios swift ios8-today-widget today-extension

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

let storage = NSUserDefaults(suiteName: "group.etc.etc.etc...")
Run Code Online (Sandbox Code Playgroud)

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

主应用程序偶尔可能会创建一个本地通知以呈现给用户.该通知有两个与之关联的操作(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()
}
Run Code Online (Sandbox Code Playgroud)

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

override func viewDidLoad() {
    super.viewDidLoad()

    // ...

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

现在,这是我的问题:

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

  2. 当通知触发时,屏幕顶部会出现一个横幅.

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

我知道这个动作在后台运行正常,并且正在对信息进行更改NSUserDefaults.

但是,即使今天的窗口小部件一直处于活动状态并始终显示在屏幕上,也不会触发重新加载操作.经过进一步调查,我可以证实,NSUserDefaultsDidChangeNotification没有被解雇(我把一个断点,并没有触发,并做了一些其他检查以及).

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

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


注1:当我改变的NSUserDefaults的信息的今天正确窗口小部件的通知火灾.

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

gab*_*ler 14

来自doc:

Cocoa包括两种类型的通知中心:NSNotificationCenter类在单个进程中管理通知.NSDistributedNotificationCenter类管理单台计算机上多个进程的通知.

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

为了在包含应用程序和扩展程序之间进行通信,您可以使用 类似的Darwin Notify Center ,它仅适用于osx.CFNotificationCenterNSDistributedNotificationCenter

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

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

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

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

let widgetController = NCWidgetController.widgetController()
widgetController.setHasContent(false, forWidgetWithBundleIdentifier: "YourTodayWidgetBundleIdentifier") 
Run Code Online (Sandbox Code Playgroud)

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