问题描述
我目前正在使用 SwiftUI 开发应用程序。
我想在每次关闭主机应用程序时从 CoreData
重新加载 widget
中的数据以更新较新的数据。
但在我的代码中,数据不会重新加载...
我该如何解决?
TimerApp.swift(主机应用程序)
import SwiftUI
import WidgetKit
@main
struct TimerApp: App {
@Environment(\.scenePhase) private var scenePhase
let persistenceController = PersistenceController.shared.managedobjectContext
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedobjectContext,persistenceController)
.onChange(of: scenePhase) { newScenePhase in
if newScenePhase == .inactive {
WidgetCenter.shared.reloadAllTimelines()
}
}
}
}
}
TimerWidget.swift(Widget 扩展)
import WidgetKit
import SwiftUI
import CoreData
struct Provider: TimelineProvider {
var moc = PersistenceController.shared.managedobjectContext
var timerEntity:TimerEntity?
init(context : NSManagedobjectContext) {
self.moc = context
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
}
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date(),timerEntity: timerEntity!)
}
func getSnapshot(in context: Context,completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(),timerEntity: timerEntity!)
return completion(entry)
}
func getTimeline(in context: Context,completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .minute,value: hourOffset,to: currentDate)!
let entry = SimpleEntry(date: entryDate,timerEntity: timerEntity!)
entries.append(entry)
}
let timeline = Timeline(entries: entries,policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let timerEntity:TimerEntity
}
struct TimerWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
return (
vstack{
Text(entry.timerEntity.task ?? "")
}
)
}
}
@main
struct TimerWidget: Widget {
let kind: String = "TimerWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind,provider: Provider(context: PersistenceController.shared.managedobjectContext)) { entry in
TimerWidgetEntryView(entry: entry)
.environment(\.managedobjectContext,PersistenceController.shared.managedobjectContext)
}
}
}
更新
我更新了我的代码以在 NSFetchRequest
函数以及下面执行 getTimeline
。
*** 由于未捕获的异常“NSinvalidargumentexception”而终止应用程序,原因:“-[TimerEntity task]: 无法识别的选择器发送到实例 0x600003175400' 终止 未捕获的 NSException 类型异常
struct Provider: TimelineProvider {
var moc = PersistenceController.shared.managedobjectContext
@State var timerEntity:TimerEntity?
init(context : NSManagedobjectContext) {
self.moc = context
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
}
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date(),timerEntity: timerEntity ?? TimerEntity())
}
func getSnapshot(in context: Context,completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .minute,timerEntity: timerEntity ?? TimerEntity())
entries.append(entry)
}
let timeline = Timeline(entries: entries,policy: .atEnd)
completion(timeline)
}
}
Xcode:版本 12.0.1
iOS:14.0
生命周期:SwiftUI 应用
解决方法
负责获取数据的代码仅在init
中:
struct Provider: TimelineProvider {
var moc = PersistenceController.shared.managedObjectContext
var timerEntity:TimerEntity?
init(context : NSManagedObjectContext) {
self.moc = context
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity = result.first
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
}
...
}
它仅在 Provider
初始化时运行。您还需要在 NSFetchRequest
函数中执行此 getTimeline
,以便更新您的 timerEntity
。
此外,如果您每次都执行 WidgetCenter.shared.reloadAllTimelines()
scenePhase
== .inactive
,它可能过于频繁并且您的 Widget 可能会停止更新。
inactive
每当您的应用从后台移动到前台(双向)时都会被调用。
尝试改用 background
- 只有在您的应用被最小化或终止时才会调用它:
.onChange(of: scenePhase) { newScenePhase in
if newScenePhase == .background {
WidgetCenter.shared.reloadAllTimelines()
}
}
注意:我建议在 WidgetCenter.shared.reloadAllTimelines()
周围添加一些保护措施,以确保不会过于频繁地刷新它。