我有一个小部件,它可以显示我在应用程序中修改的核心数据,我只希望这个小部件在主应用程序中的核心数据库刷新时刷新。
这是我的小部件
//
// RemindMeWidget.swift
// RemindMeWidget
//
// Created by Charel Felten on 05/07/2021.
//
import WidgetKit
import SwiftUI
import Intents
import CoreData
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(
date: Date(),
configuration: ConfigurationIntent(),
notes: Note.previewNotes,
realFamily: context.family
)
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(
date: Date(),
configuration: configuration,
notes: Note.previewNotes,
realFamily: context.family
)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
print("get timeline is called")
var notes: [Note] = []
let containerURL = PersistenceController.containerURL
let storeURL = containerURL.appendingPathComponent(PersistenceController.SQLiteStoreAppendix)
let description = NSPersistentStoreDescription(url: storeURL)
let container = NSPersistentCloudKitContainer(name: PersistenceController.containerName)
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
let viewContext = PersistenceController.shared.container.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let result = try viewContext.fetch(request)
// is there a nicer way to do it?
if let tmp = result as? [Note] {
notes = tmp
}
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
notes = notes.sorted { (lhs, rhs) in
if lhs.int16priority == rhs.int16priority {
return lhs.timestamp! > rhs.timestamp!
}
return lhs.int16priority > rhs.int16priority
}
let entry = SimpleEntry(
date: Date(),
configuration: configuration,
notes: notes,
realFamily: context.family
)
print("hey")
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
var notes: [Note]
var realFamily: WidgetFamily
}
struct RemindMeWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
WidgetView(notes: entry.notes, realFamily: entry.realFamily)
}
}
@main
struct RemindMeWidget: Widget {
let kind: String = "RemindMeWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
RemindMeWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct RemindMeWidget_Previews: PreviewProvider {
static var previews: some View {
RemindMeWidgetEntryView(
entry: SimpleEntry(
date: Date(),
configuration: ConfigurationIntent(),
notes: Note.previewNotes,
realFamily: .systemSmall
)
).previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
在应用程序中,我有一个设置页面,在那里我调用手动小部件刷新(现在用于调试),如下所示:
Button("Reload Widget") {
print("calling widgetcenter")
WidgetCenter.shared.reloadAllTimelines()
WidgetCenter.shared.getCurrentConfigurations({result in print(result)})
}
完整代码:
//
// SettingsView.swift
// RemindMe
//
// Created by Charel Felten on 02/07/2021.
//
import SwiftUI
import WidgetKit
struct SettingsView: View {
@Environment(\.colorScheme) var colorScheme
@ObservedObject var config: Config
var body: some View {
NavigationView {
Form {
ForEach(Priority.allRegularCases) { priority in
PriorityView(config: config, priority: priority)
}
Section(header: Text("Theme")) {
Picker(selection: $config.colorTheme, label: Text("Theme")) {
ForEach(ColorTheme.allCases, id: \.self) { value in
Text(value.rawValue).tag(value)
}
}
}
Section(header: Text("Additional Info"), footer: Text("Toggle which additional information to show below each note in the list.")) {
Toggle(isOn: $config.showNotificationTime) {
Text("Show reminders")
}
Toggle(isOn: $config.showCreationTime) {
Text("Show date")
}
Button("Reload Widget") {
print("calling widgetcenter")
WidgetCenter.shared.reloadAllTimelines()
WidgetCenter.shared.getCurrentConfigurations({result in print(result)})
}
}
}
.navigationTitle("Settings")
}
// save the settings if we leave the app (goes away from foreground)
.onReceive(
NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification),
perform: { _ in config.save() }
)
.onDisappear(perform: Note.updateAllNotes)
}
}
struct PriorityView: View {
@Environment(\.colorScheme) var colorScheme
@ObservedObject var config: Config
var priority: Priority
var body: some View {
Section(header: Text(priority.getDescription()).foregroundColor(Color.secondary)) {
Picker(selection: $config.priorityIntervals[priority.getIndex()], label: Text("Notification interval")) {
ForEach(Interval.allCases, id: \.self) { value in
Text(value.rawValue).tag(value)
}
}
switch config.priorityIntervals[priority.getIndex()] {
case .ten_minutes, .never:
EmptyView()
default:
DatePicker("Reminders at", selection: $config.priorityDates[priority.getIndex()], displayedComponents: [.hourAndMinute])
}
}
.foregroundColor(Colors.getColor(for: priority, in: .primary))
.listRowBackground(
ZStack {
colorScheme == .dark ? Color.black : Color.white
Colors.getColor(for: priority, in: .background)
}
)
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView(
config: Config()
)
}
}
单击此按钮可将以下内容打印到我的控制台:
calling widgetcenter
success([WidgetInfo:
- configuration: <ConfigurationIntent: 0x282e51dd0> {
}
- family: systemSmall
- kind: RemindMeWidget, WidgetInfo:
- configuration: <ConfigurationIntent: 0x282e51e60> {
}
- family: systemLarge
- kind: RemindMeWidget, WidgetInfo:
- configuration: <ConfigurationIntent: 0x282e51ef0> {
}
- family: systemMedium
- kind: RemindMeWidget])
但是,该小部件不会刷新,其外观如下所示:
有时候我确实刷新了,但是当我在应用程序中进行更改时,小部件没有更新。**我在getTimeline
中输入的print语句都没有被打印出来。**我真的很累,不知道我做错了什么...
我很抱歉发布了这么多代码,而不是一个较小的可复制的例子,但我只是不知道这个问题可能来自哪里。
我看了很多相关的问题,但是没有一个有帮助
- reloadAllTimelines not working with UserDefaults
- SwiftUI widget not refreshing
- How to refresh Widget when Main App is used?
如果你想看看代码,这里是:
https://github.com/charelF/RemindMeApp/tree/main/RemindMeWidget
小更新
它在模拟器中也不起作用。
我将所有更改都推送到了github,请参见https://github.com/charelF/RemindMeApp
我开了一个赏金,将奖励给谁帮助我找到我的问题。
3条答案
按热度按时间k3bvogb11#
您的代码看起来不错,但有一个例外:
在
RemindMeWidget.intentdefinition
中,您定义了一个自定义Intent“Configuration”,但此Intent没有参数。根据docu,Xcode生成以下自定义Intent:
由于Intent没有参数,因此该类没有任何
@NSManaged public var
。文件继续:
当WidgetKit调用getSnapshot(for:in:completion:)或getTimeline(for:in:completion:)时,它会传递一个自定义INIntent的示例,并使用用户选择的详细信息进行配置。游戏小部件提供程序访问Intent的属性,并将其包含在TimelineEntry中。然后,WidgetKit调用小部件配置的内容闭包,传递时间线条目,以允许视图访问用户配置的属性。
我大胆的猜测是,WidgetKit之所以混乱,是因为它没有找到任何要传递的参数。
我建议您先在没有Intent的情况下测试代码,如果这样做有效,请向Intent添加一个伪参数。
或许这能帮上忙...
ctehm74n2#
这是苹果的意图。在苹果的文件中,它说:
重新加载小部件会消耗系统资源,并由于额外的网络和处理而导致电池耗尽。若要减少这种性能影响并保持全天电池续航时间,请将请求更新的频率和数量限制在必要的范围内。
为了管理系统负载,WidgetKit使用预算来分配一天中的小部件重新加载。预算分配是动态的,并考虑了许多因素
widget的预算适用于24小时的时段。WidgetKit根据用户的日常使用模式调整24小时窗口,这意味着日常预算不一定会在午夜重置。对于用户经常查看的widget,日常预算通常包括40到70次刷新。这个速度大致相当于widget每15到60分钟重新加载一次。但由于涉及到许多因素,这些间隔通常会发生变化。
Keeping a Widget Up To Date
chhkpiq43#
我设法用ChatGPT重构了一下代码,使它工作起来。我仍然不知道确切的错误是什么。
我改变了什么
我认为是我的bug(或者至少是一个bug,似乎是Owen报告的bug,即我的小部件总是显示一个空列表):
在我的widgetview中,我只想显示前4个注解,因为我无法在小部件中容纳更多注解。
ChatGPT建议此代码只显示4条注解:
我仍然不知道为什么一个是错的,另一个不是,但这似乎阻止了我的小部件显示。然而,我设法让他们显示有时甚至用我的老方法做它,所以我认为有额外的错误。ChatGPT还建议传递
entry
对象,而不是notes
列表,不确定是否有影响。另一个bug可能是我的视图文件中有这样的代码:
我不太清楚它做了什么,但我想它可能也有影响。
不管怎样,小部件是工作的。下面是工作代码:
RemindMeWidget.swift
WidgetView.swift
完整代码:
https://github.com/charelF/RemindMeApp/tree/ace8ed293bf1e7f3af673e16a1e3076c5edd64c8
最后,我想感谢@Owen Zhao和@Reinhold Männer。我很感激你们的回答,不幸的是,虽然他们没有解决我的问题,所以我不太愿意把奖金分配给你们中的任何一个。如果我能分的话,我会分的。谢谢你们的贡献!