How can I reliably refresh WidgetKit widgets across devices after SwiftData + CloudKit synchronization?

Hey,

I'm building an app that uses App Intents to modify data stored in SwiftData and synced through CloudKit.

My expectation is that these changes should eventually be reflected in my app's widgets across all of the user's devices (iPhone, iPad, and Mac). However, I'm struggling to find a reliable way to ensure widgets reload when the underlying data changes as a result of a CloudKit sync.

Reloading widgets on the device that modifies the data works reliably. The challenge is ensuring widgets reload on other devices after the updated data has been synced through CloudKit. In practice, this doesn't appear to happen reliably.

I do not expect the user to manually open the app on those devices. I'm fine with the system launching my app in the background if that's part of the intended solution.

Is there a recommended approach for keeping widgets in sync with SwiftData and CloudKit across devices?

More specifically:

  • Can WidgetKit be notified when SwiftData receives updates from CloudKit?
  • Is there any supported way to trigger widget reloads on remote devices after a CloudKit sync?
  • If not, what is the recommended architecture for ensuring widgets stay reasonably up to date when synchronized data changes?

Things I've considered and/or tried:

  1. Calling sendChanges(_:) on CKSyncEngine from my App Intent to push changes immediately. However, this depends on an active Internet connection and doesn't address the case where changes are made offline and synchronized to CloudKit at a later time.

  2. Sending push notifications to the user's devices after an App Intent runs and using WidgetPushHandler to reload widgets. However, this requires confidence that the changes have already been uploaded to CloudKit. As far as I can tell, that's difficult or impossible to guarantee in the general case because of the issue described above.

  3. Calling fetchChanges(_:) on CKSyncEngine from the widget's timeline provider to ensure the widget has the latest data. However, the widget first needs some indication that new changes are available in CloudKit. Additionally, widget timeline reloads appear to have fairly strict execution time limits, which makes performing CloudKit synchronization work in that context seem less than ideal.

My goal is for a Shortcut run on one device to update data and have widgets on all of the user's devices reflect those changes without requiring the user to manually open the app on each device.

Am I thinking about this problem correctly, or is there a recommended pattern I'm missing?

I'd appreciate any guidance on the intended WidgetKit + SwiftData + CloudKit integration story for this scenario. Thanks!

This is worth a feedback report. In CoreData you could use remote store notifications in the widget to observe writes from the host app process. We really don't want the widget attempting to do sync work, that's up to the entitled app host.

Can WidgetKit be notified when SwiftData receives updates from CloudKit?

I would encourage you to try using the new ResultsObserver or @Query in your widget and let us know if those meet your needs when using .cloudkit with a ModelContainer.

If not, what is the recommended architecture for ensuring widgets stay reasonably up to date when synchronized data changes?

Generally speaking we expect that push notifications and background imports by the host app should support this flow.

My goal is for a Shortcut run on one device to update data and have widgets on all of the user's devices reflect those changes without requiring the user to manually open the app on each device.

We will need feedback reports with sysdiagnoses (with the CloudKit profile) to understand more about why that's not working for you.

This is worth a feedback report.

I've filed this as FB23016741. I'd love for it to be built into WidgetKit's TimelineProvider that it can reload following a CloudKit sync. I think that's a very common need.

I would encourage you to try using the new ResultsObserver or @Query in your widget and let us know if those meet your needs when using .cloudkit with a ModelContainer.

I can give it a try but I don't expect this to work. As far as I know, a widget (that is, a TimelineProvider) isn't long-lived, and as such, isn't suitable for observations.

Generally speaking we expect that push notifications and background imports by the host app should support this flow.

The challenge with using push notifications is that my data is modified from an App Intent, for example when run from Shortcuts, so I don't know when it's safe to send the notification. I could send it immediately after the App Intent completes, but before notifying other devices, I need to ensure the changes are actually available in CloudKit.

That would require the App Intent to call sendChanges(_:) on CKSyncEngine, which isn't ideal on slow or unreliable network connections. One of CloudKit's strengths is that it handles synchronization gracefully and will eventually upload changes when connectivity improves. In those cases, however, I no longer have an opportunity to send a push notification once the data finally becomes available in CloudKit.

It just occurred to me that by saying

Generally speaking we expect that push notifications and background imports by the host app should support this flow.

...you might not refer to explicitly sending a push notification to the widget and causing it to reload with WidgetPushHandler, but rather relying on CloudKit's default push notifications and it's background imports.

The challenge with that is that I can't reload widgets as a result of those, because WidgetKit's reloadAllTimelines() has no effect when invoked while the app is in the background.

It's worth noting that reloadAllTimelines() works when running an App Intent in the host app with supportedModes: IntentModes = [.background, .foreground(.dynamic)] on the intent, but not when the app receives a notification in the background.

How can I reliably refresh WidgetKit widgets across devices after SwiftData + CloudKit synchronization?
 
 
Q