【问题标题】:NSUserDefaultsDidChangeNotification and Today ExtensionsNSUserDefaultsDidChangeNotification 和 Today Extensions
【发布时间】:2015-04-01 19:50:52
【问题描述】:

我正在开发一个带有 Today Extension 的 iPhone 应用程序。该应用程序有一个模型模块,可以从NSUserDefaults 加载/保存到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. 我在该横幅上向下滑动以显示这两个操作,然后选择我之前提到的一个(今天的小部件仍然在屏幕上)。

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

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

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

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


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

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

【问题讨论】:

  • 为什么将通知注册放在viewDidLoad而不是初始化器中?
  • @Rikkles 好吧,没有具体原因;这就是我通常做的地方。但在这种情况下,这并不重要,因为通知操作是在今天小部件的视图控制器上调用 viewDidLoad 之后发生的。
  • 为什么要手动重新加载小部件?每当小部件显示在屏幕上时,操作系统都会通过widgetPerformUpdateWithCompletionHandler 从小部件请求更新,因此您可以从那里的用户默认值中获取新值并在那里更新。你也可以试试KVO works
  • 也许题外话,但如果你想在扩展程序和它的包含应用程序之间进行通信,看看这个项目:github.com/cxa/AppExtensionCommunicator。免责声明:我创建了这个项目。
  • @cxa 我已经下载了,但是没有用

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


【解决方案1】:

来自doc这里:

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

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

为了在包含应用程序和扩展程序之间进行通信,您可以使用 Darwin 通知中心 CFNotificationCenterNSDistributedNotificationCenter 类似,仅适用于 osx。

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

在今天的扩展中,使用CFNotificationCenterAddObserver观察CFNotification,收到后会调用callback,其中一个NSNotification必须发布,因为callback是C风格的函数,“userInfo”不能传入CFNotification,收到这个NSNotification对象后,开始从文件中读取数据,用于更新Notification中心的今日扩展视图。

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

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

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

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

【讨论】:

  • 我相信他指的是 iOS,因为它被标记为 iOS,并且他正在模拟器中运行。
  • @JeremyPope,是的,我想是的,链接中的解决方案和代码都是针对iOS的。
  • 所以基本上 NSUserDefaultsDidChangeNotification 不会触发,因为应用程序和扩展是两个独立的进程。这并不让我感到惊讶,但同时我期待这“只是工作”......我尝试了最后一个想法(使用setHasContent),它似乎也没有工作,虽然我必须调查它更彻底。谢谢!
  • 他们都为我工作,最后一个想法,我呆在通知中心,小部件暂时不在那里,然后以更新的值重新出现。 :)。
  • 如果您需要 Watch 和 Today 扩展的监听器和数据共享库,请使用这个简洁的库:github.com/mutualmobile/MMWormhole
猜你喜欢
  • 1970-01-01
  • 2014-07-26
  • 2014-11-17
  • 1970-01-01
  • 2013-05-28
  • 1970-01-01
  • 2018-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多