Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 691
Sarvesh Avatar asked Jul 06 '20 11:07

Sarvesh


People also ask

How do you refresh widgets on iOS 14?

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.

How do you refresh an Apple widget?

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.

How often do widgets refresh iOS?

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.


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.

like image 183
Sarvesh Avatar answered Oct 21 '22 15:10

Sarvesh


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.

like image 20
Piyush Chopra Avatar answered Oct 21 '22 13:10

Piyush Chopra


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.

like image 3
Jordan H Avatar answered Oct 21 '22 14:10

Jordan H