简体   繁体   English

如何处理访问同一个 CoreData 存储的多个小部件实例?

[英]How do I deal with multiple widget instances accessing the same CoreData store?

Background Info背景资料

I have a main application, which writes a single entry to a database contained in an App Group (we'll call them "DB1", and "Group1", respectively).我有一个主应用程序,它将单个条目写入应用程序组中包含的数据库(我们分别称它们为“DB1”和“Group1”)。 To the same project, I have added an iOS 14 Home Screen Widget extension.在同一个项目中,我添加了一个 iOS 14 主屏幕小部件扩展。 This extension is then added to Group1.然后将此扩展添加到 Group1。

All three sizes (small, medium, large) show the same information from the DB1 entry, just rearranged, and for the smaller widgets, some parts omitted.所有三种尺寸(小、中、大)都显示来自 DB1 条目的相同信息,只是重新排列,对于较小的小部件,省略了一些部分。

Problem问题

The problem is, if I have multiple instances of the widget (say a small and a medium), then when I rebuild the target, and it loads/runs, I get CoreData errors such as the following:问题是,如果我有多个小部件实例(比如小型和中型),那么当我重建目标并加载/运行时,我会收到 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}

Relevant Code相关代码

Here's the code for the Timeline function这是时间线 function 的代码

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)
        }
    }

And here is the loadTimelineEntry function这是 loadTimelineEntry function

(persistentContainer gets initialized in the init() of WidgetDataSource, the class that holds loadTimelineEntry function) (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)
                }
            }
        })
    }

What I've Tried Honestly, not much.老实说,我尝试过的东西不多。 I had gotten this same error before I put the widget into the Group1.在将小部件放入 Group1 之前,我遇到了同样的错误。 That time it was due to the main app already having DB1 created on it's first run, then on the subsequent widget run, it looked in it's own container, didn't find it, and attempted to create it's own DB1, and the OS didn't let it due to them having the same name.那是因为主应用程序在第一次运行时已经创建了 DB1,然后在随后的小部件运行中,它查看了它自己的容器,没有找到它,并试图创建它自己的 DB1,而操作系统没有不要因为他们有相同的名字而让它。

Widgets being a fairly new feature, there are not many questions regarding this specific use case, but I'm sure someone out there has a similar setup.小部件是一个相当新的功能,关于这个特定用例的问题并不多,但我敢肯定有人有类似的设置。 Any help or tips will be highly appreciated!任何帮助或提示将不胜感激!

Thanks to Tom Harrington's comment, I was able to solve the warnings by adding a check at the top of my loadTimelineEntry function like so:感谢 Tom Harrington 的评论,我能够通过在我的 loadTimelineEntry function 顶部添加一个检查来解决警告,如下所示:

if !persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty {
   //Proceed with fetch requests, etc. w/o loading stores
} else {
   //Original logic to load store, and fetch data
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM