Vic*_*lov 5 core-data swift cloudkit widgetkit swiftui
该应用程序使用 Core Data + CloudKit。当应用程序启动时,WidgetKit 会从核心数据中获取正确的条目,但是当我从主应用程序向核心数据添加新条目并调用WidgetCenter.shared.reloadTimelines()更新的条目时,不会获取到旧数据,并且旧数据会显示在小部件内。
主应用程序和小部件目标具有相同的应用程序组,两个目标都启用了 iCloud 功能。
当我WidgetCenter.shared.reloadTimelines()从主应用程序调用小部件重新加载(添加到小部件视图的计时器设置为零)但从核心数据获取时,不会返回新添加的条目。然后我重新启动应用程序并获取正确的条目。
为什么当 WidgetCenter.shared.reloadTimelines() 被调用时它不获取正确的条目并且仅在应用程序重新启动时才起作用?
小部件的代码:
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.")
}
}
Run Code Online (Sandbox Code Playgroud)
这是核心数据的代码:
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])
}
}
Run Code Online (Sandbox Code Playgroud)
事实证明,我所要做的就是在进行获取之前将Core Data 的上下文更新为当前存储生成(在 WidgetKit 文件中的 getTimeline() 函数):
try? Storage.viewContext.setQueryGenerationFrom(.current)
Storage.viewContext.refreshAllObjects()
Run Code Online (Sandbox Code Playgroud)
请在此处阅读更多相关内容:https ://developer.apple.com/documentation/coredata/accessing_data_when_the_store_has_changed
| 归档时间: |
|
| 查看次数: |
979 次 |
| 最近记录: |