如何处理访问同一 CoreData 存储的多个小部件实例?
How do I deal with multiple widget instances accessing the same CoreData store?
背景信息
我有一个主应用程序,它将单个条目写入应用程序组(我们将分别称为“DB1”和“Group1”)中包含的数据库。对于同一个项目,我添加了一个 iOS 14 主屏幕小部件扩展。然后将此扩展程序添加到 Group1。
所有三种尺寸(小、中、大)都显示来自 DB1 条目的相同信息,只是重新排列,对于较小的小部件,省略了一些部分。
问题
问题是,如果我有多个小部件实例(比如小型和中型),那么当我重建目标时,它 loads/runs,我会收到如下 CoreData 错误:
<NSPersistentStoreCoordinator: 0x---MEM--->: Attempting recovery from error encountered during addPersistentStore: Error Domain=NSCocoaErrorDomain Code=134081 "(null)" UserInfo={NSUnderlyingException=Can't add the same store twice}
相关代码
这是时间线功能的代码
public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
//Set up widget to refresh every minute
let currentDate = Date()
let refreshDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
WidgetDataSource.shared.loadTimelineEntry { entry in
guard let e = entry else { return }
let entries: [TimelineEntry] = [e]
let timeline = Timeline(entries: entries, policy: .after(refreshDate))
completion(timeline)
}
}
这里是 loadTimelineEntry 函数
(persistentContainer在WidgetDataSource的init()中初始化,class持有loadTimelineEntry函数)
func loadTimelineEntry(callback: @escaping (TimelineEntry?) -> Void) {
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
print("Loading persistent stores")
var widgetData: [WidgetData]
if let error = error {
print("Unresolved error \(error)")
callback(nil)
} else {
let request = WidgetData.createFetchRequest()
do {
widgetData = try self.persistentContainer.viewContext.fetch(request)
guard let data = widgetData.first else { callback(nil); return }
print("Got \(widgetData.count) WidgetData records")
let entry = TimelineEntry(date: Date())
callback(entry)
} catch {
print("Fetch failed")
callback(nil)
}
}
})
}
我试过的
老实说,不多。在将小部件放入 Group1 之前,我遇到了同样的错误。当时是因为主应用程序已经在其第一个 运行 上创建了 DB1,然后在后续的小部件 运行 上,它查看了自己的容器,但没有找到,并尝试创建它是自己的 DB1,并且 OS 没有允许它,因为它们具有相同的名称。
小部件是一个相当新的功能,关于这个特定用例的问题并不多,但我敢肯定有人有类似的设置。任何帮助或提示将不胜感激!
感谢 Tom Harrington 的评论,我能够通过在我的 loadTimelineEntry 函数顶部添加一个检查来解决警告,如下所示:
if !persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty {
//Proceed with fetch requests, etc. w/o loading stores
} else {
//Original logic to load store, and fetch data
}
背景信息
我有一个主应用程序,它将单个条目写入应用程序组(我们将分别称为“DB1”和“Group1”)中包含的数据库。对于同一个项目,我添加了一个 iOS 14 主屏幕小部件扩展。然后将此扩展程序添加到 Group1。
所有三种尺寸(小、中、大)都显示来自 DB1 条目的相同信息,只是重新排列,对于较小的小部件,省略了一些部分。
问题
问题是,如果我有多个小部件实例(比如小型和中型),那么当我重建目标时,它 loads/runs,我会收到如下 CoreData 错误:
<NSPersistentStoreCoordinator: 0x---MEM--->: Attempting recovery from error encountered during addPersistentStore: Error Domain=NSCocoaErrorDomain Code=134081 "(null)" UserInfo={NSUnderlyingException=Can't add the same store twice}
相关代码
这是时间线功能的代码
public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
//Set up widget to refresh every minute
let currentDate = Date()
let refreshDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
WidgetDataSource.shared.loadTimelineEntry { entry in
guard let e = entry else { return }
let entries: [TimelineEntry] = [e]
let timeline = Timeline(entries: entries, policy: .after(refreshDate))
completion(timeline)
}
}
这里是 loadTimelineEntry 函数
(persistentContainer在WidgetDataSource的init()中初始化,class持有loadTimelineEntry函数)
func loadTimelineEntry(callback: @escaping (TimelineEntry?) -> Void) {
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
print("Loading persistent stores")
var widgetData: [WidgetData]
if let error = error {
print("Unresolved error \(error)")
callback(nil)
} else {
let request = WidgetData.createFetchRequest()
do {
widgetData = try self.persistentContainer.viewContext.fetch(request)
guard let data = widgetData.first else { callback(nil); return }
print("Got \(widgetData.count) WidgetData records")
let entry = TimelineEntry(date: Date())
callback(entry)
} catch {
print("Fetch failed")
callback(nil)
}
}
})
}
我试过的 老实说,不多。在将小部件放入 Group1 之前,我遇到了同样的错误。当时是因为主应用程序已经在其第一个 运行 上创建了 DB1,然后在后续的小部件 运行 上,它查看了自己的容器,但没有找到,并尝试创建它是自己的 DB1,并且 OS 没有允许它,因为它们具有相同的名称。
小部件是一个相当新的功能,关于这个特定用例的问题并不多,但我敢肯定有人有类似的设置。任何帮助或提示将不胜感激!
感谢 Tom Harrington 的评论,我能够通过在我的 loadTimelineEntry 函数顶部添加一个检查来解决警告,如下所示:
if !persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty {
//Proceed with fetch requests, etc. w/o loading stores
} else {
//Original logic to load store, and fetch data
}