18

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.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
Sarvesh
  • 401
  • 1
  • 3
  • 8

3 Answers3

12

This seems to be fixed in iOS 14 beta 2. The widget now correctly updates when refreshAllTimelines() is called from the notification response.

Sarvesh
  • 401
  • 1
  • 3
  • 8
4

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.

Jordan H
  • 52,571
  • 37
  • 201
  • 351
3

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.

  • 1
    It works well on Lists, Buttons, etc, just not in the notification didRecieve response function. I tried Dispatch Main as well, doesn't work. – Sarvesh Jul 08 '20 at 01:31
  • And what about [TimelineEntryRelevance](https://developer.apple.com/documentation/widgetkit/timelineentryrelevance)? Try setting it as `TimelineEntryRelevance(score: 100)` where 100 means the information displayed to the user is very important, so the engine will know to update the widget on priority. – Piyush Chopra Jul 09 '20 at 03:55
  • 1
    I set it to 60 and it seems to be working. But I wonder why it didn't work properly before. I thought calling refreshAllTimelines() forces a refresh no matter what the relevance is. – Sarvesh Jul 09 '20 at 07:37
  • Okay it's working without the relevance score as well. I think it was fixed in iOS 14 beta 2. – Sarvesh Jul 09 '20 at 08:01
  • 1
    You have to import WidgetKit. Check [this](https://github.com/piyushChopra07/WidgetDemoSwiftUI) for reference – Piyush Chopra Jul 23 '20 at 08:49