I'm making a home screen widget for iOS 14 using the new WidgetKit and I want my widget timeline to refresh when the user responds to a notification.
This is what my code looks like currently:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == "actionIdentifier" {
print("notification response received")
WidgetCenter.shared.reloadAllTimelines()
}
completionHandler()
}
But my widget is not updated when the user responds to the notification. The print statement gets printed, so I know my app is receiving the response. The widget also gets refreshed when I call reloadAllTimeLines()
anywhere else in my app, so I'm sure my widget extension is implemented correctly. But it's not updating in the above scenario.
Is this a bug or am I doing something wrong? Or is there another way to reload a widget timeline after the user responds to a notification.
Tap On The Widget Sometimes all you need to do to update the information display on a widget is to tap on it and open the app. When the app opens, its content will refresh, which should refresh the widget too.
Users can always manually force a refresh by tapping on the refresh button in the widget zoom view, or simply by double tapping on a widget in the main dashboard view. A manual refresh overrides all limit checking and forces the widget to fetch new data immediately.
For a widget the user frequently views, a daily budget typically includes from 40 to 70 refreshes. This rate roughly translates to widget reloads every 15 to 60 minutes, but it's common for these intervals to vary due to the many factors involved. The system takes a few days to learn the user's behavior.
This seems to be fixed in iOS 14 beta 2. The widget now correctly updates when refreshAllTimelines() is called from the notification response.
Try calling that under Dispatch Main, or try calling it with a delay of, to test, say 2 seconds.
I am calling the same on SwiftUI's List as
let listArray = ["Item1", "Item2", "Item3"]
List(listArray) { listObject in
Button(action: {
WidgetCenter.shared.reloadAllTimelines()
}) {
HStack {
Text(listObject)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
.padding()
}
}
}
And it's working like charm.
tldr; Reloading your widgets while the app is in the background does seem to work, however, it may not update immediately.
I encountered this recently and added logging to determine what's going on. If curious I did this by adding NSLog
everywhere relevant, replicating the issue, collecting a sysdiagnose, and examining the system_logs. I tested this scenario which seems to be the most extreme: trigger the notification action from my Apple Watch with the iPhone locked, in low power mode, with the app not open in the background, and 3 widgets added to the Home Screen.
What I found is the app was launched, the notification action was processed, and reloadAllTimelines()
was triggered as expected. But getTimelines
in the widget was not called - not until about 24 minutes later! So it will update eventually. And note this was not due to my timeline reload policy - I have that set to only refresh once a day at midnight.
iOS is surely trying to be smart about when it'll process reload requests to preserve battery life etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With