上下文
我一直在使用此示例项目来玩 Today 扩展。
该应用程序非常简单:
- 在包含的应用程序中,您有一个待办事项列表,您可以将其标记为已完成 在
- "今天"小组件中,您会看到相同的列表,但您可以使用分段控件在已完成和未完成的项目之间切换。
我的目标如下:每当在容器应用或小部件中发生数据更改时,我希望两者都反映更改:
- 如果我在容器应用中将项目标记为已完成,然后下拉通知中心,则应更新小组件
- 当我在小部件中执行相同操作然后返回应用程序时,应用程序的状态应该更新
实施
我知道,容器应用程序和扩展在各自的进程中运行,这意味着两个约束:
-
NSUserDefaultsDidChangeNotification
是没有用的。 - 管理内存中的模型实例毫无用处。
我还知道,为了访问共享容器,两个目标都必须选择加入同一组 ID 下的应用程序组授权。
数据访问由嵌入式框架TodoKit管理。它不是将属性保留在内存中,而是直接NSUserDefaults
适当的值:
public struct ShoppingItemStore: ShoppingStoreType {
private let defaultItems = [
ShoppingItem(name: "Coffee"),
ShoppingItem(name: "Banana"),
]
private let defaults = NSUserDefaults(suiteName: appGroupId)
public init() {}
public func items() -> [ShoppingItem] {
if let loaded = loadItems() {
return loaded
} else {
return defaultItems
}
}
public func toggleItem(item: ShoppingItem) {
let initial = items()
let updated = initial.map { original -> ShoppingItem in
return original == item ?
ShoppingItem(name: original.name, status: !original.status) : original
}
saveItems(updated)
}
private func saveItems(items: [ShoppingItem]) {
let boxedItems = items.map { item -> [String : Bool] in
return [item.name : item.status]
}
defaults?.setValue(boxedItems, forKey: savedDataKey)
defaults?.synchronize()
}
private func loadItems() -> [ShoppingItem]? {
if let loaded = defaults?.valueForKey(savedDataKey) as? [[String : Bool]] {
let unboxed = loaded.map { dict -> ShoppingItem in
return ShoppingItem(name: dict.keys.first!, status: dict.values.first!)
}
return unboxed
}
return nil
}
}
问题所在
以下是有效的方法:
- 当我在主应用程序中修改列表,然后停止模拟器,然后从 Xcode 启动 Today 目标时,它会反映正确的状态。反之亦然。
这将验证我的应用组是否已正确设置。
但是,当我在主应用程序中更改某些内容,然后下拉通知中心时,它完全不同步。这是我不明白的部分。
我的视图直接从共享容器获取数据。每当发生更改时,我都会立即更新共享容器中的数据。
我错过了什么?如何正确同步这两者?我的数据访问类不管理任何状态,但我不明白为什么它的行为不正确。
附加信息
我知道MMWormhole。不幸的是,这对我来说不是一个选择,因为我需要在不包括任何第三方解决方案的情况下获得适当的功能。
这篇很棒的文章涵盖了这个主题,并且有可能,我需要使用
NSFilePresenter
,尽管它看起来很麻烦,而且我还不完全了解机制。我真的希望,有一个比这个更简单的解决方案。
好吧,我在这里学到了两件事:
首先,始终仔细检查您的权利,我的不知何故搞砸了,这就是共享容器表现如此尴尬的原因。
第二:尽管不会调用 viewWillAppear(_:)
,但在关闭通知中心时,仍可以从应用委托触发更新:
func applicationDidBecomeActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName(updateDataNotification, object: nil)
}
然后在视图控制器中:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserverForName(updateDataNotification, object: nil, queue: NSOperationQueue.mainQueue()) { (_) -> Void in
self.tableView.reloadData()
}
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
更新"今日"小组件很简单:每次下拉通知中心时,都会调用viewWillAppear(:_)
,以便您可以在那里查询新数据。
我将很快更新 GitHub 上的示例项目。