I created a widget for my app that’s meant to be refreshed day-after-day after midnight, in addition to when a go to (AKA name) is began, ended, or canceled. The midnight replace is working effective, however the reload triggered by the perform calls within the mum or dad app just isn’t resulting in any change within the UI, regardless of WidgetCenter.shared.reloadTimelines(ofKind: "Name")
being known as once I set a breakpoint within the debugger.
Options I’ve tried that have not labored:
- Calling
WidgetCenter.shared.reloadAllTimelines()
as an alternative of specifying widget variety to ensure all bases are coated when reloading - Ready with the app within the foreground for a couple of minutes after triggering a change to make sure that the widget has time to refresh
- Calling
reloadTimelines(ofKind:)
from strategies within the AppDelegate (this truly works just about immediately, however is not a viable answer as a result of it might shortly run via the day by day reload finances) - Transferring the decision to
reloadTimelines(ofKind:)
elsewhere within the code
Right here is my code for getTimeline:
func getTimeline(in context: Context,
completion: @escaping (Timeline<Entry>) -> Void) {
let currentDate = Date()
let midnight = Calendar.present.startOfDay(for: currentDate)
let nextMidnight = Calendar.present.date(byAdding: .day, worth: 1, to: midnight)!
let entry: Entry
entry = Entry(context: WidgetEnvironment.shared
.coreDataManager.viewContext, date: .now)
let timeline = Timeline(entries: [entry], coverage: .after(nextMidnight))
completion(timeline)
}
Right here is the struct for the widget:
@principal
struct CallWidget: Widget {
let variety: String = "Name"
let coreDataManager = WidgetEnvironment.shared
.coreDataManager
var physique: some WidgetConfiguration {
StaticConfiguration(variety: variety, supplier: Supplier()) { entry in
CallWidgetEntryView(entry: entry)
}
.configurationDisplayName("Name")
.description("Present or subsequent name")
.supportedFamilies([.systemMedium])
}
}
Right here is the initializer for my ViewModel, which I take advantage of as an entry by passing within the context and date and creating the variables I would like for the widget UI:
extension CallWidgetEntryView {
class CallWidgetViewModel: TimelineEntry {
personal var context: NSManagedObjectContext
var date: Date
var callStatus: CallStatus
var callStatusLabel = Labels.getLabelFor(label: Labels.noCallScheduled)
var storeName = ""
var handle = ""
var addressLine2 = ""
var callStartTime = Date.now
var backgroundColor = Coloration("grey")
var modelTime = ""
var lastCallDate = Date.now
init(context: NSManagedObjectContext, date: Date) {
self.context = context
self.date = date
if let name = Name.inProgress(context: WidgetEnvironment.shared
.coreDataManager.viewContext) {
callStatus = .currentCall
callStatusLabel = Labels.getLabelFor(label: Labels.callInProgress)
storeName = name.account?.title ?? ""
handle = name.account?.handle ?? ""
addressLine2 = name.account?.addressLine2 ?? ""
callStartTime = name.actualStartDate ?? .now
modelTime = ""
lastCallDate = .now
backgroundColor = Coloration("inexperienced")
} else if let account = Account.fetchWithNextCall(on: .now, in: WidgetEnvironment.shared
.coreDataManager.viewContext) {
callStatus = .nextCall
callStatusLabel = Labels.getLabelFor(label: Labels.nextScheduledCall)
storeName = account.title ?? ""
handle = account.handle ?? ""
addressLine2 = account.addressLine2
callStartTime = .now
modelTime = DateComponentsFormatter.modelCallTimeFormatter
.string(from: DateComponents(minute: Int(account.modelTime))) ?? ""
lastCallDate = account.dateOfLastCompletedStoreCall ?? .now
backgroundColor = Coloration("blue")
} else /* No extra calls at present */ {
callStatus = .noCall
callStatusLabel = Labels.getLabelFor(label: Labels.noCallScheduled)
backgroundColor = Coloration("grey")
}
}
// ...
}
}
And listed below are the perform calls (every from a separate file):
Begin Go to
func startCall(scheduled: Bool,
location: CLLocation? = nil,
locationExceptionReason: String? = nil) {
let context = AppEnvironment.shared.mesh.viewContext
// ...
context.performAndWait {
// ...
attempt? context.save()
}
// ...
WidgetCenter.shared.reloadTimelines(ofKind: "Name")
// ...
}
Finish Go to
func endCall(_ name: Name,
location: CLLocation? = nil,
locationExceptionReason: String? = nil) {
guard let context = name.managedObjectContext else { return }
context.performAndWait {
// ...
attempt? context.save()
_ = attempt? ResetHasChangesProcessor.resetHasChanges(context: context)
}
// ...
WidgetCenter.shared.reloadTimelines(ofKind: "Name")
let mesh = AppEnvironment.shared.mesh
DDLogInfo("Performing sync")
mesh.carry out {
FullSyncTask(changesOnly: true,
mesh: mesh)
}
}
Cancel Go to
func cancelCall() {
defer { navigationController?.popViewController(animated: true) }
guard let context = name?.managedObjectContext else { return }
context.performAndWait { [weak self] in
// ...
self?.name?.cancel()
attempt? context.save()
_ = attempt? ResetHasChangesProcessor.resetHasChanges(context: context)
}
WidgetCenter.shared.reloadTimelines(ofKind: "Name")
}
I’m questioning if possibly the widget would not have time to react to the up to date context earlier than I reload it, however I do not know methods to go about testing or resolving this.
Any and all assist could be enormously appreciated.