Widgets & Live Activities

RSS for tag

Discuss how to manage and implement Widgets & Live Activities.

WidgetKit Documentation

Posts under Widgets & Live Activities subtopic

Post

Replies

Boosts

Views

Activity

Unable to activate ActivityKit
Appears during code compilation Provisioning profile "iOS Team Provisioning Profile: ..*" doesn't include the com.apple.developer.ActivityKit entitlement, Has anyone encountered or resolved a similar issue where the ActiveKit feature was not found in the developer's identifier, despite not being activated in the developer's system?
0
1
54
Sep ’25
Live Activities Push-to-Start flows
Good morning, We are implementing Live Activities in a push-to-start flow. We wrap the listener for push to start tokens in a high priority task: if ptsListenerTask == nil || ptsListenerTask?.isCancelled == true { ptsListenerTask = Task(priority: .high) { [weak self] in for await pushToken in Activity<LiveAuctionAttributes>.pushToStartTokenUpdates { //Send token to back-end } } I've tried a few variations of this and they work well on most devices. I have seen a couple of devices that refuse to issue a push to start token. The user will have logging for the init flow and starting the PTS listener then the logs just go silent, nothing happens. One thing that seemed to work was getting the user to start a Live Activity manually (from our debugging tool) then the PTS token gets issued. This is not very reliable and working a mock live activity into the flow for obtaining a PTS token is a poor solution. Is anyone else seeing this and is there a known issue with obtaining PTS tokens? Thanks! Brad
8
1
205
1w
What should your server do when apns returns a 410 Unregistered error for your token?
For our Live Activity Tokens, when we fire a payload, often apns is returning a response of 410 unregistered. Docs are saying: The device token is inactive for the specified topic. There is no need to send further pushes to the same device token, unless your application retrieves the same device token, refer to Registering your app with APNs Questions: Why does this happen? Does it only happen because the user changed their permission? Or there are other reasons? And when it does happen, what should we do about it? A. Should we keep the token on the server? Because perhaps the user will change their permission and the token becomes valid? That could leave us with lots of invalid tokens and us firing at them unnecessarily. Docs do say: Don’t retry notification responses with the error code BadDeviceToken, DeviceTokenNotForTopic, Forbidden, ExpiredToken, Unregistered, or PayloadTooLarge. B. Or should we remove the token from the server? That then requires app to re-register the token. But the problem with that is: When I went into App's settings from OS settings and toggled push notifications to on, the app was not launched into the background nor killed i.e. it requires explicit app launch by the user to re-register itself which isn't ideal... It means a user may turn on notifications from the OS settings and then assume that their push notifications should be back in business, but that won't happen if you toggle things from OS settings.
1
0
145
3w
App widget not appearing in widget list (intermittent)
There seems to be a long running issue with WidgetKit where some users don't see the widget when trying to add to their Home Screen. (even after opening the app for the first time). I have been able to reproduce myself intermittently, and typically restarting the phone or re-installing the app fixes the problem. However, some of my users have encountered this and end up requesting refunds because they think the app is broken. Has anybody else experienced this issue? Would be great to get this bug resolved as it's frustrating for users.
1
2
552
Sep ’25
[Widget] ChronoKit.InteractiveWidgetActionRunner.Errors.runnerClientError `There is no metadata for `
In our widget we include a button with an intent, making a network call to refresh some shared data in its perform(). Whether the call finishes in time or not is not important to us, what matters more is that the widget gets reloaded at the end and displays whatever data it has available with its transition animation. On iOS18.0 we see the widget being reloaded but on the latest version 18.4 this doesn't happen anymore. Going through the logs, on both devices we see this same flow: default 2025-04-10 15:05:26.853674 +0300 WidgetRenderer_Default Evaluating dispatch of UIEvent: 0x300e002a0; type: 0; subtype: 0; backing type: 11; shouldSend: 1; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.853691 +0300 WidgetRenderer_Default Sending UIEvent type: 0; subtype: 0; to windows: 1 default 2025-04-10 15:05:26.853702 +0300 WidgetRenderer_Default Sending UIEvent type: 0; subtype: 0; to window: <WidgetRenderer.WidgetWindow: 0x5689b4000>; contextId: 0x8E401B8A default 2025-04-10 15:05:26.853735 +0300 SpringBoard Evaluating dispatch of UIEvent: 0x300af9420; type: 0; subtype: 0; backing type: 11; shouldSend: 1; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.853836 +0300 SpringBoard Sending UIEvent type: 0; subtype: 0; to windows: 1 default 2025-04-10 15:05:26.853864 +0300 SpringBoard Sending UIEvent type: 0; subtype: 0; to window: <_UISystemGestureWindow: 0xb5a20d000>; contextId: 0x5A4C4C23 default 2025-04-10 15:05:26.854862 +0300 SpringBoard Evaluating dispatch of UIEvent: 0x300aeeca0; type: 0; subtype: 0; backing type: 11; shouldSend: 1; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.854866 +0300 WidgetRenderer_Default [Timeline[<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget:systemMedium::321.00/148.00/20.20:(null)]--A76785AED3F9::0xb5bc4c000)] Handle action default 2025-04-10 15:05:26.854892 +0300 SpringBoard Sending UIEvent type: 0; subtype: 0; to windows: 1 default 2025-04-10 15:05:26.854901 +0300 SpringBoard Sending UIEvent type: 0; subtype: 0; to window: <SBHomeScreenWindow: 0xb5ad60000>; contextId: 0x71D69FA2 default 2025-04-10 15:05:26.855015 +0300 WidgetRenderer_Default Handle action: <private> default 2025-04-10 15:05:26.855360 +0300 SpringBoard Allowing tap for icon view '<private>' default 2025-04-10 15:05:26.855376 +0300 SpringBoard Not allowing tap gesture to begin because we're not editing, the custom view controller's user interaction is enabled, and the effective icon alpha isn't zero. default 2025-04-10 15:05:26.856940 +0300 SpringBoard Icon touch ended: <private> default 2025-04-10 15:05:26.857474 +0300 backboardd contact 1 presence: none default 2025-04-10 15:05:26.857826 +0300 chronod Received action <private> for interaction <WidgetRenderSession--4632871937259503361-scene::C1F20222-CC99-45CC-B074-A76785AED3F9::0xb5bc4c000-[<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget:systemMedium::321.00/148.00/20.20:(null)]> default 2025-04-10 15:05:26.858381 +0300 chronod [<WidgetRenderSession--4632871937259503361-scene::C1F20222-CC99-45CC-B074-A76785AED3F9::0xb5bc4c000-[<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget:systemMedium::321.00/148.00/20.20:(null)]>] Handle interaction: <private> default 2025-04-10 15:05:26.858436 +0300 chronod Pausing reloads for: [<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget] default 2025-04-10 15:05:26.869525 +0300 chronod [0xd180b2440] activating connection: mach=true listener=false peer=false name=com.apple.linkd.registry default 2025-04-10 15:05:26.870054 +0300 WidgetRenderer_Default Evaluating dispatch of UIEvent: 0x300e002a0; type: 0; subtype: 0; backing type: 11; shouldSend: 0; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.870124 +0300 SpringBoard Evaluating dispatch of UIEvent: 0x300af9420; type: 0; subtype: 0; backing type: 11; shouldSend: 0; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.870198 +0300 SpringBoard Evaluating dispatch of UIEvent: 0x300aeeca0; type: 0; subtype: 0; backing type: 11; shouldSend: 0; ignoreInteractionEvents: 0, systemGestureStateChange: 0 default 2025-04-10 15:05:26.871831 +0300 linkd Accepting XPC connection from PID 129 for service "com.apple.linkd.registry" default 2025-04-10 15:05:26.871840 +0300 linkd [0x410cbe6c0] activating connection: mach=false listener=false peer=true name=com.apple.linkd.registry.peer[129].0x410cbe6c0 info 2025-04-10 15:05:26.876032 +0300 chronod Client requested ( "<LNFullyQualifiedActionIdentifier: 0xd17321b40, bundleIdentifier: <edited-bundle-identifier>, actionIdentifier: ReloadBalanceIntent>" ), got { } default 2025-04-10 15:05:26.877178 +0300 chronod [0xd180b2440] invalidated because the current process cancelled the connection by calling xpc_connection_cancel() default 2025-04-10 15:05:26.877377 +0300 linkd [0x410cbe6c0] invalidated because the client process (pid 129) either cancelled the connection or exited Then it followa with this on iOS18.4 : error 2025-04-10 15:21:32.964920 +0300 chronod [<WidgetRenderSession-7817322460413849944-scene::B5E4D7C4-91E1-4656-8175-C3C3C1CB894D::0xc733b8000-[<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget:systemLarge::364.00/382.00/23.00:(null)~(null)]>] Encountered error when handling interaction: ChronoKit.InteractiveWidgetActionRunner.Errors.runnerClientError(Error Domain=WFLinkActionWorkflowRunnerClientErrorDomain Code=1 "There is no metadata for ReloadBalanceIntent in `<edited-bundle-identifier>`" UserInfo={NSLocalizedDescription=There is no metadata for ReloadBalanceIntent in `<edited-bundle-identifier>`}) default 2025-04-10 15:21:32.964958 +0300 chronod [<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget] Resuming reloads. Reload state paused -> clean info 2025-04-10 15:21:32.965013 +0300 chronod [interactionFailed] All diagnostics are disabled. Whereas on iOS18.0 it follows with a simplified error: error 2025-04-10 15:05:26.879005 +0300 chronod [<WidgetRenderSession--4632871937259503361-scene::C1F20222-CC99-45CC-B074-A76785AED3F9::0xb5bc4c000-[<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget:systemMedium::321.00/148.00/20.20:(null)]>] Encountered error when handling interaction: Error Domain=ChronoKit.InteractiveWidgetActionRunner.Errors Code=1 default 2025-04-10 15:05:26.879065 +0300 chronod Resuming reloads for: [<edited-bundle-identifier>::<edited-bundle-identifier>.<Widget>:widget] but afterwards we see many lines describing the reload process. So it turns out that the intent fails(?) to execute on both OSes but iOS18.0 triggers a reload even so, which fits our purposes. What could the issue be? The intent is pretty standard, it contains only the title, localizedDescription and is defined only inside the widget.
1
2
120
Apr ’25
reloadAllTimelines() is broken on watchOS 11.5
I am experiencing an issue with my watchOS app. My application updates a shared file, accessible by both the main app and the WidgetKit extension, during a WatchConnectivity background task. Following this update, I call reloadAllTimelines(). This functionality worked as expected on watchOS 10, even without Developer Mode enabled. However, after updating my device to watchOS 11.5, this API appears to be broken. My reasons for believing this API is broken are threefold: The functionality worked reliably on watchOS 10. On watchOS 11.5, enabling "WidgetKit Developer Mode" (found under Settings > Developer) resolves the issue, and my complications update correctly. When I enter watch face edit mode, the snapshot/preview displayed (which utilizes the same underlying logic as the timeline API) shows the correct data. This indicates that the data has been successfully received from the phone, and the widget is indeed reading the updated shared file. Despite this, the actual widget view during its resting phase never updates. It's worth noting that TimelineReloadPolicy functions as expected, but I cannot rely on this for my use case. My widget requires immediate updates upon receiving new data. Apple, please investigate this behavior.
1
2
154
Jun ’25
Migrate Widgets from StaticConfiguration to IntentConfiguration
New features in WatchOS 26 with configurable widgets make it more important than ever that apps adopt IntentConfiguration options where applicable. I develop an app with an Apple Watch complication/widget on many many user's Watch faces around the world. I've completed updating my code to support WidgetKit and remove ClockKit. However, I face huge issues adding support for users to configure their widget/complications. If I update a widget to go from StaticConfiguration to IntentConfiguration, even when keeping the "kind" string the same, the widget disappears from the Watch face. This is an unacceptable user experience meaning I can't proceed with the migration. The problem is users will expect me to offer configuration in the Watch face soon for their widget/complication. Currently this process is done in a sub-optimal way in the app itself. A similar issue exists on iOS where the widget will just "freeze" indefinitely is migrated. This issue still occurs on the iOS 26 and WatchOS 26 betas. So how to move this forward. This has been discussed previously here: https://developer.apple.com/forums/thread/661247 I've mentioned it at WidgetKit labs I've filed feedback last year: FB13880020 I've filed feedback this year: FB18180368 It seems really important this gets fixed for developers to adopt these new features, is there any other migration route I'm missing or a workaround that would mitigate this seemingly big problem.
4
1
188
Sep ’25
New push notifications for widgets seem too limited for actual production-level apps
I was very excited to see the addition of push notifications for widgets. However upon further inspection, the way it is implemented seems too limiting for real life apps. I have an app for time tracking with my own backend. The app syncs with my backend in the main executable (main target). My widgets are more lightweight as they only access data in the shared app container, but they don't perform sync with the server directly to avoid race conditions with the main app. I was under the impression that the general direction of the platform is to be doing most things in the main app target (also App Intents work that way for the most part), so the fact that the WidgetPushHandler just calls the widget's method to reload the timeline is very unfortunate. In an ideal scenario I also need the main app to be 'woken up' to perform the sync with the server, and once that's done I'd update the widget's timeline and where I would just read data from the shared app container. So, my questions are: What is the recommended way of updating the widgets when this push notification arrives in the case that the main app target needs to perform the sync first? Is there any way how to detect that the method func timeline(for configuration: InteractiveTrackingWidgetConfigurationAppIntent, in context: Context) was called as a result of the push notification being received? Can I somehow schedule a background task from the widget's reloadTimeline() function? How can I get the push token later, in case that I don't save it right away the first time the WidgetPushHandler's pushTokenDidChange() is called? Thank you for your work on this and hopefully for your answers. FB19356256
2
2
128
Sep ’25
iOS 26 - Widget not updated with respect to main app's system preferred language
Hi, My iOS app's home screen widget content was implemented to base on the preferred language of my main app (e.g. my app has the following preferred language options with this order English, Japanese, Traditional Chinese, Korean, Simplify Chinese). Say the main app is currently using English as their preferred language, I can change the preferred language in the iOS Settings -> Apps -> My App -> Preferred Language. My widget's content will respect to the preferred language option that I selected with only exception if I switch back to English language and my Widget's content won't get updated. The Main app content is always update with respect to the selected preferred language. My app and widget is working without any issue in iOS 18. Other things that I had discovered during my testing under iOS 26, the "first" language appeared in my preferred language always being the issue (e.g. if the first language is Japanese , once I change to other languages and than switch back to Japanese, my widget content won't respect to this but the main app content are ok). Any one has a similar issues regarding the preferred language?
3
2
236
1w
Live activity custom format timer
We need a live activity to countdown our time in formats like 2h 30m -> 2h 29m -> 2h 28m etc 58m -> 57m I see this kind in pictures in official documentation. to make a functional timer is: Text(context.state.startTime, style: .timer) But it's very limited when it comes to formatting. This timer .relative includes seconds which we don't want Text(futureDate, style: .relative) Also format we need is h instead of hour, m instead of min
0
2
53
Sep ’25
Live Activity PushToken Not Callback
I have start new live activity with push notifications,but sometimes this code"activity.pushTokenUpdates" can not callback to me,so I can't update the activity with push notifications. And I already click “Allow” button in my live activity widget.How can I solve this problem. Here are my code: fileprivate func observeLiveActivityForRemoteCreate() { // obverser pushToStartToken Task { if #available(iOS 17.2, *) { var beforeToken = "" for await pushToken in Activity<HLPlatformActivityAttributes>.pushToStartTokenUpdates { let pushTokenStr = pushToken.map{String(format: "%02.2hhx", arguments: [$0])}.joined() // avoid send duplication if beforeToken == pushTokenStr { return } beforeToken = pushTokenStr // send pushToStartToken to service await HLPlatformLiveActivityBridge.registerLiveActivity(withAttributesName: self.activityAttributesName, pushToStartToken: pushToken, seq: seqCreate(), pushType: .jPush) } }else { // Fallback on eralier versions } // obverser live activity update Task { for await activity in Activity<HLPlatformActivityAttributes>.activityUpdates { if let businessLiveActivityId = activity.attributes.businessLiveActivityId, let liveActivityId = activity.attributes.liveActivityId { Task { var beforeToken = "" for await pushToken in activity.pushTokenUpdates { // here the problem:sometimes pushToken not update to me let pushTokenStr = pushToken.map{String(format: "%02.2hhx", arguments: [$0])}.joined() // avoid send duplication if beforeToken == pushTokenStr { return } beforeToken = pushTokenStr // send pushToken to service } } Task { for await stateUpdate in activity.activityStateUpdates { if stateUpdate == .active { // live activity create } } } } } } }
1
3
182
Jul ’25
Dynamic Options for Configurable Widgets
Hello, I'm trying to create a widget using the WidgetKit framework. In this part, I'm using Intents along with a DynamicOptionsProvider. As shown in the Medium article below, I want to present multiple options when "Edit Widget" is tapped: https://levelup.gitconnected.com/swiftui-configurable-widget-to-let-our-user-choose-4a54e398f42f However, in this example, the options are provided statically. What I want to achieve is to display a list of devices based on the selected HomeId after the user selects a Home. I’ve set up the interface accordingly, but when I select a Home, the device list does not update. How can I make this work? The two options should be dependent on each other.
0
4
158
May ’25
Push tokens from push-to-start Live Activities require user permissions?
Just wanted to clarify some expected behaviors here. It seems that there are two distinct behaviors for Live Activity flows for freshly installed apps. When you start a Live Activity for the first time and the user hasn't yet clicked on Allow/Don't Allow in the activity interface, there are two different sequences: Starting a Live Activity locally Request a Live Activity locally via Swift Live Activity starts .pushTokenUpdates is immediately triggered, even if the Allow/Don't Allow buttons appear under the Activity UI Starting a Live Activity via push-to-start Send a push-to-start notification to launch a Live Activity Live Activity starts .pushTokenUpdates is not triggered, and .pushToken returns nil. If a user clicks on Allow in the Activity UI, only then is .pushTokenUpdates triggered.
2
4
156
Oct ’25
Get update token from the OS when the Live Activity is started from the backend, without the user explicitly providing "Allow" or "Always Allow" consent from the lock screen
We are currently using Live Activities in our app and supporting both of the following use cases: Starting a Live Activity directly from the app using ActivityKit APIs. Starting a Live Activity from the backend using the start token. In the first case (initiated from the app), the OS generates an update token, and we are able to continuously update the Live Activity via our backend—even if the user has not explicitly provided "Allow" or "Always Allow" consent from the lock screen. This works as expected. In the second case (initiated from the backend), if the user does provide consent ("Allow" or "Always Allow") from the lock screen, we receive the update token and can continue updating the Live Activity. However, if the user does not provide consent, the OS does not provide the update token, and we are unable to send further updates. Question: Is it possible to receive the update token from the OS when the Live Activity is started from the backend, without the user explicitly providing "Allow" or "Always Allow" consent from the lock screen? We would appreciate any clarification or official documentation related to this behavior. Thank you!
0
5
147
Sep ’25
watchOS: AppIntents.IntentRecommendation description ignored when applying a .watchface
When we use AppIntents to configure WidgetKit complications, the description we provide in IntentRecommendation is ignored after applying a .watchface file that includes those intent configurations. In the Watch app, under Complications, the labels shown next to each slot do not match the actual complications on the face—they appear to be the first strings returned by recommendations() rather than the selected intent configuration. Steps to Reproduce Create an AppIntent used by a WidgetKit complication (e.g., .accessoryRectangular). Provide multiple intent recommendations with distinct descriptions: struct SampleIntent: AppIntent { static var title: LocalizedStringResource = "Sample" static var description = IntentDescription("Sample data") @Parameter(title: "Mode") var mode: String static func recommendations() -> [IntentRecommendation<Self>] { [ .init(intent: .init(mode: "A"), description: "Complication A"), .init(intent: .init(mode: "B"), description: "Complication B"), .init(intent: .init(mode: "C"), description: "Complication C") ] } func perform() async throws -> some IntentResult { .result() } } Add two of these complications to a Modular Duo face (or any face that supports multiple slots), each with different intent configurations (e.g., A in one slot, B in another). Export/share the face to a .watchface file and apply it on another device. Open the Watch app → the chosen face → Complications. Expected Each slot’s label in Complications reflects the specific intent configuration on the face (e.g., “Complication A”, “Complication B”), matching what the complication actually renders. Actual The labels under Complications do not match the visible complications. Instead, the strings shown look like the first N items from recommendations(), regardless of which configurations are used in each slot. Notes The complications themselves render correctly on-watch; the issue is the names/labels displayed in the Watch app UI after applying a .watchface. Filed Feedback: FB20915258
3
3
113
2w