The app uses Core Data + CloudKit. When the app gets launched WidgetKit file fetches correct entries from Core Data but when I add new entries to Core Data from the main app and call WidgetCenter.shared.reloadTimelines() updated entries don't get fetched and old data is displayed inside the widget.
Main app and Widget target have the same App group, iCloud capability enabled for both targets.
When I call WidgetCenter.shared.reloadTimelines() from the Main app Widget reloads (timer added to Widget view sets to zero) but fetch from Core Data doesn't return newly added entries. Then I restart the app and correct entries get fetched.
Why doesn't it fetch correct entries when WidgetCenter.shared.reloadTimelines() gets called and only works when app is relaunched?
Widget's code:
Here is the code for Core Data:
Main app and Widget target have the same App group, iCloud capability enabled for both targets.
When I call WidgetCenter.shared.reloadTimelines() from the Main app Widget reloads (timer added to Widget view sets to zero) but fetch from Core Data doesn't return newly added entries. Then I restart the app and correct entries get fetched.
Why doesn't it fetch correct entries when WidgetCenter.shared.reloadTimelines() gets called and only works when app is relaunched?
Widget's code:
Code Block import WidgetKit import SwiftUI import CoreData struct Provider: TimelineProvider { func placeholder(in context: Context) -> HabitsEntry { HabitsEntry(date: Date(), habitsCount: 0) } func getSnapshot(in context: Context, completion: @escaping (HabitsEntry) -> ()) { let entry = HabitsEntry(date: Date(), habitsCount: 0) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { let managedObjectContext = Storage.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: "DailyHabit") let predicate = Storage.getCurrentDatePredicate() request.predicate = predicate var habits = 0 do { habits = try managedObjectContext.count(for: request) } catch let error as NSError {print ("Could not fetch \(error), \(error.userInfo)")} let entry = [HabitsEntry(date: Date(), habitsCount: habits)] let timeline = Timeline(entries: entry, policy: .never) completion(timeline) } } struct HabitsEntry: TimelineEntry { let date: Date var habitsCount: Int } struct HabitsHomeWidgetEntryView : View { var entry: Provider.Entry var body: some View { Text(entry.date, style: .timer) Text("There are \(entry.habitsCount) daily habits") } } @main struct HabitsHomeWidget: Widget { let kind: String = "HabitsHomeWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in HabitsHomeWidgetEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") } }
Here is the code for Core Data:
Code Block import UIKit import CoreData import WidgetKit class Storage { static let shared = Storage() static var persistentContainer: NSPersistentContainer { return Storage.shared.persistentContainer } static var viewContext: NSManagedObjectContext { return persistentContainer.viewContext } lazy var persistentContainer: NSPersistentContainer = { let container: NSPersistentContainer let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupName)! let storeURL = containerURL.appendingPathComponent("NoRegrets.sqlite") let description = NSPersistentStoreDescription(url: storeURL) if isICloudContainerAvailable { container = NSPersistentCloudKitContainer(name: Constants.persistentContainerName) } else { container = NSPersistentContainer(name: Constants.persistentContainerName) } description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) description.shouldMigrateStoreAutomatically = true description.shouldInferMappingModelAutomatically = true let storeDescription = description container.persistentStoreDescriptions = [storeDescription] container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { return } }) container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = true do { try container.viewContext.setQueryGenerationFrom(.current) } catch { print("Failed to pin viewContext to the current generation: \(error)") } return container }() var isICloudContainerAvailable: Bool { FileManager.default.ubiquityIdentityToken != nil } // MARK: - Core Data Saving support func saveContext() { let context = persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError print("Saving error: \(nserror)") } } } } // MARK: - Helper methods extension Storage { func getCurrentUser() -> User? { let fetchRequest = User.createFetchRequest() fetchRequest.fetchLimit = 1 fetchRequest.sortDescriptors = [NSSortDescriptor(key: "objectID", ascending: false)] let user = try? Storage.viewContext.fetch(fetchRequest).first return user } class func getCurrentDatePredicate() -> NSPredicate { var calendar = Calendar.current calendar.timeZone = NSTimeZone.local let dateFrom = calendar.startOfDay(for: Date()) let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom) let fromPredicate = NSPredicate(format: "date >= %@", dateFrom as NSDate) let toPredicate = NSPredicate(format: "date < %@", dateTo! as NSDate) return NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate, toPredicate]) } }