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.

I am no expert so take it with a pinch of salt.

I don't have a solution when the app is closed.

What will not work:

  • ResultsObserver or any of the SwiftUI's @State etc will not work in widgets because this is loaded from a timeline and this is all synchronized to disk, so no way for the widget to react when underlying data changes, that is what makes widgets so efficient.

Best I can come up with (it is not ideal):

  • When you open the app observe for core-data sync notifications and reload relevant widgets.
  • Worst case set a timeline refresh policy to a date that is 1 hour from the current time, so that the user has at least the data that is updated 1 hour ago.

Summary

  • I don't know how to reload when deviceA updates and deviceB's app is closed
  • If you come up with a solution would love to know

@Frameworks Engineer @simonbs

  • For widget push update to work you need your own server which needs to initiate this push notification, which will send to APNS (Apple Push Notification Server)
  • Then APNS will send to the relevant devices
  • Your server can't send a push notification for every data change, somehow you need to have a way to collate because it is budgeted

Questions

  • For this to even work in your case, How does you need to configure the server? (because your data is stored in CloudKit)?
  • How will your server be aware of any data changes, should the app send HTTP request to your server then your server initiates the push notification?
  • Or can you do this even without a server by the app directly talking to APNS (I don't think it is possible)?

My understanding (please confirm / clarify)

To keep widget data up to date:

  • I feel it needs to be a combination of all 4
    1. TimelineReloadPolicy
    2. When your app is open it can reload timelines
    3. When your app is open and data changes on a different device then observe cloudkit sync notifications and reload timeline
    4. Widget Push Updates - needs your own server with some logic on your end, where your server think enough data has changed over time to initiate a push notification and your app needs to handle this.
  • 1, 2, and 3 are can be entirely done on the app side. 4 needs server side work and app work.

I agree with the sentiment here. It feels like cross-device widget refreshes are one of the remaining missing pieces for developers who want to stay entirely within Apple’s ecosystem.

With SwiftData + CloudKit, it’s possible to build a fully server-less app, which is fantastic. But when it comes to keeping widgets updated across devices, the solution often becomes “run a server and send APNs pushes.”

I actually filed feedback about this last year (FB21094995) requesting a CloudKit-style remote widget refresh mechanism. The motivation was very similar to what’s being discussed here: if CloudKit can eventually sync the data, it would be nice if there were also an Apple-managed way to notify widgets on other devices that the underlying data has changed and should be refreshed.

Thanks for raising this topic. I’d love to see a first-party solution in this area.

@fahad-sh Thanks a lot filing the Feedback, yes it would be great to have all Apple solution.

I agree it feels a bit odd to have end to end sync with cloudkit out of the box and then have your server just to initiate push notifications.

On a different note, something to keep in mind:

  • Based on the latest WidgetKit Foundations, frequent reloads when the app is in foreground might be throttled.
  • If data is changed It is preferred to reload timeline when app goes to background / is not the active app.
  • I think it is to save battery life.

Another problem / Proposal

  • Since all timeline reloads are budgeted, so right now we have to reload all timelines belonging to a certain widget kind.
  • It would be nice for Apple to provide an option (API) only to reload certain widgets based on certain parameter configuration values, so that we don't need to reload all timeline for widget kind.
  • Hopefully that would result consuming less of the widget reloaded and less of the quota and better battery life
How can I reliably refresh WidgetKit widgets across devices after SwiftData + CloudKit synchronization?
 
 
Q