如何在宿主应用程序关闭时从 Widget iOS 14 中的 CoreData 重新加载获取数据
How to reload fetch data from CoreData in Widget iOS 14 when host App closes
我目前正在使用 SwiftUI 开发应用程序。
我想在 widget
中重新加载来自 CoreData
的数据,以便在每次关闭主机应用程序时更新更新的数据。
但在我的代码中,数据不会重新加载...
我该如何解决?
TimerApp.swift(主机APP)
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(插件扩展)
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)
}
}
}
已更新
我更新了我的代码以在 getTimeline
函数和下面执行 NSFetchRequest
。
但是在我的代码中,当我构建这个项目时出现如下错误:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TimerEntity task]:
unrecognized selector sent to instance 0x600003175400' terminating
with uncaught exception of type 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 (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()
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, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, 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
时才为运行。您还需要在 getTimeline
函数中执行此 NSFetchRequest
,因此您的 timerEntity
已更新。
此外,如果您每次都执行 WidgetCenter.shared.reloadAllTimelines()
scenePhase
== .inactive
,这可能太频繁了,您的 Widget 可能会停止更新。
inactive
每当您的应用程序从后台移动到前台(双向)时都会调用。
尝试改用 background
- 只有当您的应用程序最小化或终止时才会调用它:
.onChange(of: scenePhase) { newScenePhase in
if newScenePhase == .background {
WidgetCenter.shared.reloadAllTimelines()
}
}
注意:我建议在 WidgetCenter.shared.reloadAllTimelines()
周围添加一些保护措施,以确保不会 过于频繁地刷新它 。
我目前正在使用 SwiftUI 开发应用程序。
我想在 widget
中重新加载来自 CoreData
的数据,以便在每次关闭主机应用程序时更新更新的数据。
但在我的代码中,数据不会重新加载...
我该如何解决?
TimerApp.swift(主机APP)
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(插件扩展)
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)
}
}
}
已更新
我更新了我的代码以在 getTimeline
函数和下面执行 NSFetchRequest
。
但是在我的代码中,当我构建这个项目时出现如下错误:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TimerEntity task]: unrecognized selector sent to instance 0x600003175400' terminating with uncaught exception of type 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 (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()
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, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, 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
时才为运行。您还需要在 getTimeline
函数中执行此 NSFetchRequest
,因此您的 timerEntity
已更新。
此外,如果您每次都执行 WidgetCenter.shared.reloadAllTimelines()
scenePhase
== .inactive
,这可能太频繁了,您的 Widget 可能会停止更新。
inactive
每当您的应用程序从后台移动到前台(双向)时都会调用。
尝试改用 background
- 只有当您的应用程序最小化或终止时才会调用它:
.onChange(of: scenePhase) { newScenePhase in
if newScenePhase == .background {
WidgetCenter.shared.reloadAllTimelines()
}
}
注意:我建议在 WidgetCenter.shared.reloadAllTimelines()
周围添加一些保护措施,以确保不会 过于频繁地刷新它 。