How to reload timeline of iOS 14 widget from notification response?

Viewed 11044

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.

3 Answers

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.

Related