简体   繁体   中英

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). To the same project, I have added an iOS 14 Home Screen Widget extension. This extension is then added to 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.

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:

<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

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

(persistentContainer gets initialized in the init() of WidgetDataSource, the class that holds loadTimelineEntry function)

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. 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.

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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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