A Live Activity relies on its app being in the background , but contrary to a widget it does not have a timeline.
Our app "in the making" is sandboxed and does not need to receive ActivityKit push notifications
How is the app "maintained in life" and not entirely killed for the duration of the activity - Apple documentation says up to 8 hours ?
Is it necessary for us to do something to maintain it, like to trick the system , play a white noise audio background ...?
ActivityKit
RSS for tagHelp people keep track of tasks and events that they care about with Live Activities on the Lock Screen, the Dynamic Island, and in StandBy.
Posts under ActivityKit tag
133 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
My start live activity CURL is not starting my live activity and I keep getting a decoding failure even though my curl matches my content state so my live activity is not starting. heres my CURL
--header "apns-topic: MuscleMemory.KimchiLabs.com.push-type.liveactivity" \
--header "apns-push-type: liveactivity" \
--header "apns-priority: 10" \
--header "authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IjI4MjVTNjNEV0IifQ.eyJpc3MiOiJMOTZYUlBCSzQ2IiwiaWF0IjoxNzU3NDYwMzQ2fQ.5TGvDRk5ZYLsvncjKwXIZYN78X88v5lCwX4fRvfl1QXjwv8tOtO2uoId27LQahXA3zqjruu_2YoOfqEtrppKXQ" \
--data '{
"aps": {
"timestamp": '"$(date +%s)"',
"event": "start",
"content-state": {
"plain_text": "hello world",
"userContentPage": ["hello world"]
},
"alert": { "sound": "chime.aiff" }
},
"attributes-type": "KimchiKit.DynamicRepAttributes",
"attributes": {}
}' \
--http2 https://api.sandbox.push.apple.com/3/device/802fe7b4066e26b51ede7188a7077a9603507a0fa6ee8ffda946a864e75aa139602861538d6fb12100afbe9a3338d6c7c799d947dfacb2ee835f0339ecdc3165c9ed7e54839f5a3b89b76a011f5826cc
and here is my content state
public struct ContentState: Codable, Hashable {
public var plainText: String
public var userContentPage: [String]
public enum CodingKeys: String, CodingKey {
case plainText = "plain_text"
case userContentPage
}
public init(plainText: String, userContentPage: [String]) {
self.plainText = plainText
self.userContentPage = userContentPage
}
}
public init() {}
}
While the activityBackgroundTint modifier is intended to set the background color of a Live Activity, it often fails to dynamically update, leaving the activity with an incorrect background. Replacing it with
ZStack {
Color(.background)
....
}
solves the problem, but this is a workaround. The activityBackgroundTint modifier is still needed, at a minimum, so that the "Allow Live Activity for the app" extension does not have the default color.
On earlier iOS versions Live Activity displays correctly according to mode set.
Can't find an opened issue for that
version: iOS 26
device: iPhone 16
After opening 5 live activities using live activity push-to-start push notifications, I clear them from lock screen.
From this point forward I am unable to open new live activities.
Device log shows:
Could not create a new activity from push notification: ActivityKit.ActivityAuthorizationError.targetMaximumExceeded
How can I handle such use case?
shouldn't the count of opened live activities be reduced once the user clears the live activities? is this a bug?
Live Activity: no value in pushTokenUpdates and pushToStartTokenUpdates not called after app restart
When running application after installation I get a value in pushTokenUpdates and also the call to pushToStartTokenUpdates returns with the token value.
But, if I kill the app and restart it again, there is no value in pushTokenUpdates and the call to pushToStartTokenUpdates does not return.
Am I suppose to use the push to start token from the previous application run?
is there a different solution?
When sending multiple push to start notifications to start a live activities in a short time frame, after around 10 pushes live activities are no longer being started.
Device logs show the following entry:
Push-to-start budget exceeded for com.att.tlv.myatt::pushToStart; not starting activity
What can be done to be able to open more live activities via push-to-start in a short time frame (increase the push-to-start budget)?
Can this be related to the development environment and it will not happen on production?
NSSupportsLiveActivitiesFrequentUpdates is already set to YES
Does the alert only serve the following purposes:
required for start events
require for any event that needs to alert the user (with sound or having the live activity show in expanded presentation)
while its content DO NOT matter. Only the presence of the field and its attributes matter.
The only time its content matter are when the receiving device is an Apple Watch.
Is that correct?
Hi, I'm having this issue that I have have not been able to figure out as I've gone through a checklist and it seems I have everything in place, but im sending my pushToStartTokenUpdates token to a server and im able to test starting a live activity via CURL where it shows my push token going through and even my test payload but after a while I get this issue in my logs where it fails to find a live activity
Push notifications are set up, im sending my token to APN and im even able to start live activities locally.
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
User Notifications
ActivityKit
I'm struggling to understand what the impact of this flag is.
Docs only say:
For devices running iOS 18 and iPadOS 18 or later, you can add input-push-token: 1 to your payload to start a Live Activity and receive a new push token. After you receive a new push token, you can use it to send updates to a Live Activity.
But things were working fine for iOS 17. Right?
Does it somehow make the OS emit update tokens faster/more successfully?
Should I include in all start, update, end events?
LiveActivity using colorScheme to adapt to dark mode in iOS 26 system does not work
The system keeps returning. mark, unable to switch
Hey there, i start a live activity with notification , and but sometime it can not update, it happens sometime and make me so confuse;
like about 2025-08-19 +8.0 09:20:52
updatetoken : 40ead3fd4dd4a934548c5455c645fc6920edfaa0ddad98abd619dc1d2662389c15d3dc3b420bdec733d9438d25ae61cb55601ae64292b3064a71ffb8d0355633f3f0f2254e6f7438282ce83366d8430c
app can not accept push update msg, and APNs has show success when server send update msg to APP;
how can i check the APNS to APP?
Thanks
The docs are conflicting.
https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications#End-the-Live-Activity-with-a-custom-dismissal-date says:
When you end a Live Activity, by default the Live Activity appears on the Lock Screen for up to four hours after it ends to allow people to glance at their phone to refer to the latest information.
However here it says:
https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities#Understand-constraints
A Live Activity can be active for up to eight hours unless its app or a person ends it before this limit. After the eight-hour limit, the system automatically ends the Live Activity, and immediately removes it from the Dynamic Island. However, the Live Activity remains on the Lock Screen until a person removes it or for up to four additional hours before the system removes it — whichever comes first. As a result, a Live Activity remains on the Lock Screen for a maximum of 12 hours.
So is it 4 hrs OR '8 for Dynamic Island vs 12 for Lock Screen'?
I'm developing an app which will show the driver ETA and rendering the progress bar of the current ETA.
Intuitively the backend server sends the push notification every 3 seconds through firebase cloud message service to APNS so that the device can refresh the dynamic island smoothly.
But the live activity doesn't refresh as frequently as the backend service does.
It should refresh every 3 seconds but it turns out like refresh 30 ~ 60 seconds, sometimes it didn't refresh at all.
any body facing the same?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
APNS
User Notifications
ActivityKit
I'm developing an app which will show the driver ETA and rendering the progress bar of the current ETA.
Intuitively the backend server sends the push notification every 3 seconds through firebase cloud message service to APNS so that the device can refresh the dynamic island smoothly.
But the live activity doesn't refresh as frequently as the backend service does.
It should refresh every 3 seconds but it turns out like refresh 30 ~ 60 seconds, sometimes it didn't refresh at all.
any body facing the same?
When using the ManagedSettings API to block apps everything is blocked as expected (the app itself, Notifications, Live Activities on the Lock Screen etc) except for Compact Live Activities of those apps (that are shown in the Dynamic Island). I feel the expected behavior would be to block also the Compact Live Activities.
Our use case:
In Spoilerblock we want to prevent users from being exposed to spoilers before they've had time to watch for example a sports game.
Current workaround:
Right now the best we can do is to ask the user to disable Live Activities for apps that could expose results, to not risk being exposed to a spoiler.
When I use the Activity.request method to start the Dynamic Island widget, there have been continuous crash reports online, Could you please provide some ideas on how to fix this crash? Thanks!
Crash backtraces:
0ActivityKitblock_copy_helper.101 (in ActivityKit)+21416
1CombinePublishers.FlatMap.Outer.receive(B.Output) (in Combine)+296
2Combineprotocol witness for Subscriber.receive(A.Input) in conformance Publishers.FlatMap<A, B>.Outer<A1> (in Combine)+20
3CombinePublishers.HandleEvents.Inner.receive(A.Output) (in Combine)+204
4Combineprotocol witness for Subscriber.receive(A.Input) in conformance Publishers.HandleEvents<A>.Inner<A1> (in Combine)+20
5CombineFilterProducer.receive(B) (in Combine)+2508
6Combineprotocol witness for Subscriber.receive(A.Input) in conformance FilterProducer<A, B, C, D, E> (in Combine)+20
7Combinepartial apply for closure #4 (C) in Publishers._Merged.request(Subscribers.Demand) (in Combine)+52
8CombinePublishers._Merged.guardedApplyDownstream<A>((C)) (in Combine)+180
9CombinePublishers._Merged.receive(A, Int) (in Combine)+400
10CombinePublishers._Merged.Side.receive(A) (in Combine)+20
11CombineJust.Inner.request(Subscribers.Demand) (in Combine)+692
12Combineprotocol witness for Subscription.request(Subscribers.Demand) in conformance Just<A>.Inner<A1> (in Combine)+20
13CombinePublishers._Merged.receive(subscription: Subscription, _: Int) (in Combine)+496
14CombinePublishers._Merged.Side.receive(subscription: Subscription) (in Combine)+20
15CombineJust.receive<A>(subscriber: A1) (in Combine)+420
16CombinePublishers.Merge.receive<A>(subscriber: A1) (in Combine)+380
17CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
18CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
19CombinePublishers.RemoveDuplicates.receive<A>(subscriber: A1) (in Combine)+220
20CombinePublishers.HandleEvents.receive<A>(subscriber: A1) (in Combine)+300
21CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
22CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
23CombinePublishers.FlatMap.receive<A>(subscriber: A1) (in Combine)+416
24CombinePublishers.RemoveDuplicates.receive<A>(subscriber: A1) (in Combine)+220
25CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
26CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
27CombinePublishers.CompactMap.receive<A>(subscriber: A1) (in Combine)+440
28CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
29CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
30CombinePublishers.SetFailureType.receive<A>(subscriber: A1) (in Combine)+420
31CombinePublishers.FlatMap.receive<A>(subscriber: A1) (in Combine)+416
32CombinePublishers.RemoveDuplicates.receive<A>(subscriber: A1) (in Combine)+220
33CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
34CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
35CombinePublishers.CompactMap.receive<A>(subscriber: A1) (in Combine)+440
36CombinePublisherBox.receive<A>(subscriber: A1) (in Combine)+104
37CombineAnyPublisher.receive<A>(subscriber: A1) (in Combine)+60
38CombinePublishers.ReceiveOn.receive<A>(subscriber: A1) (in Combine)+660
39CombinePublisher.sink(receiveCompletion: (Subscribers.Completion<A.Failure>), receiveValue: (A.Output)) (in Combine)+316
40ActivityKitblock_copy_helper.101 (in ActivityKit)+33364
41ActivityKitblock_copy_helper.101 (in ActivityKit)+26716
42ActivityKitblock_copy_helper.101 (in ActivityKit)+53192
43ActivityKitblock_copy_helper.101 (in ActivityKit)+52408
44ActivityKitblock_destroy_helper.25 (in ActivityKit)+1140
45ActivityKitblock_destroy_helper.62 (in ActivityKit)+8220
46ActivityKitblock_destroy_helper.62 (in ActivityKit)+9364
47libswiftDispatch.dylibpartial apply for thunk for @callee_guaranteed () -> (@out A, @error @owned Error) (in libswiftDispatch.dylib)+24
48libswiftDispatch.dylibthunk for @callee_guaranteed () -> (@out A, @error @owned Error)partial apply (in libswiftDispatch.dylib)+12
49libswiftDispatch.dylibclosure #1 () in closure #1 (()) in OS_dispatch_queue._syncHelper<A>(fn: (()), execute: (), rescue: (Error)) (in libswiftDispatch.dylib)+188
50libswiftDispatch.dylibpartial apply for thunk for @callee_guaranteed () -> () (in libswiftDispatch.dylib)+24
51libswiftDispatch.dylibthunk for @escaping @callee_guaranteed () -> () (in libswiftDispatch.dylib)+24
52libdispatch.dylib__dispatch_client_callout (in libdispatch.dylib)+16
53libdispatch.dylib__dispatch_lane_barrier_sync_invoke_and_complete (in libdispatch.dylib)+52
54libswiftDispatch.dylibimplicit closure #2 (()) in implicit closure #1 (OS_dispatch_queue) in OS_dispatch_queue.asyncAndWait<A>(execute: ()) (in libswiftDispatch.dylib)+188
55libswiftDispatch.dylibpartial apply for implicit closure #2 (()) in implicit closure #1 (OS_dispatch_queue) in OS_dispatch_queue.sync<A>(execute: ()) (in libswiftDispatch.dylib)+72
56libswiftDispatch.dylibOS_dispatch_queue._syncHelper<A>(fn: (()), execute: (), rescue: (Error)) (in libswiftDispatch.dylib)+400
57libswiftDispatch.dylibOS_dispatch_queue.asyncAndWait<A>(execute: ()) (in libswiftDispatch.dylib)+136
58libswiftDispatch.dylibOS_dispatch_queue.sync<A>(execute: ()) (in libswiftDispatch.dylib)+60
59ActivityKitblock_destroy_helper.62 (in ActivityKit)+3272
60ActivityKitblock_destroy_helper.62 (in ActivityKit)+724
61ActivityKit___swift_store_extra_inhabitant_indexTm (in ActivityKit)+24928
62ActivityKit___swift_store_extra_inhabitant_indexTm (in ActivityKit)+23900
63 XXXApp static LiveActivityService.request()
......
Hi Apple team,
I'm experiencing a persistent issue with writing to UserDefaults from a widget extension on iOS. Here's the situation:
I've set up an App Group: group.test.blah
The main app has the correct entitlement and can read/write from UserDefaults(suiteName:) using this group successfully.
I can read the value written by the app from the widget (e.g., "testFromApp": "hiFromApp").
The widget extension has the same App Group enabled under Signing & Capabilities.
The provisioning profile reflects the App Group and the build installs successfully on a real device.
The suite name is correct and matches across both targets.
I’ve confirmed via FileManager.default.containerURL(...) that the app group container resolves properly.
When I try to write from the widget extension like this
let sharedDefaults = UserDefaults(suiteName: "group.test.blah")
sharedDefaults?.set("hiFromWidget", forKey: "testFromWidget")
...I get this error in the console:
Couldn't write values for keys (
testFromWidget
) in CFPrefsPlistSource<0x1140d2880> (Domain: group.test.blah, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): setting preferences outside an application's container requires user-preference-write or file-write-data sandbox access
Questions:
What could still cause the widget extension to lack write access to the app group container, even though it reads just fine?
Are there any internal sandboxing nuances or timing-related issues specific to Live Activity widgets that could explain this?
Is this a known limitation or platform issue?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
Extensions
Entitlements
ActivityKit
Files and Storage
Background
I'm developing an iOS app with Live Activities that allows users to select custom background images. While these custom images display correctly in widgets, they fail to appear in Live Activities on both Lock Screen and Dynamic Island, despite successful image loading and data transfer.
Technical Details
iOS Version: Testing on iOS 17.2+
Image Storage: Using App Group shared container for image sharing between main app and widget extension
Image Loading: Successfully confirmed via logs - images load correctly with proper dimensions
UI Framework: SwiftUI with ActivityKit
What Works
✅ Custom images display correctly in Home Screen widgets
✅ Built-in bundled images work in Live Activities
✅ Image data successfully transfers via App Group shared container
✅ Image loading logs show successful UIImage creation with correct dimensions
What Doesn't Work
❌ Custom user images don't display in Live Activities (Lock Screen)
❌ Custom user images don't display in Dynamic Island
❌ Images appear as black/gray background despite successful loading
Code Implementation
I've tried multiple approaches:
1. SwiftUI Image approach:
Image(uiImage: customImage)
.resizable()
.aspectRatio(contentMode: .fill)
2. containerBackground API approach:
.containerBackground(for: .widget) {
Image(uiImage: customImage)
.resizable()
.aspectRatio(contentMode: .fill)
}
3. UIKit wrapper approach:
struct UIImageViewWrapper: UIViewRepresentable {
let image: UIImage
func makeUIView(context: Context) -> UIImageView {
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
return imageView
}
func updateUIView(_ uiView: UIImageView, context: Context) {
uiView.image = image
}
}
Observations
Images load successfully (confirmed via console logs)
File paths are correct and accessible
Same image loading code works perfectly in widgets
When testing with UIKit approach, a red prohibition icon appears, suggesting system restrictions
Questions
Is there a technical limitation preventing user-provided images from displaying in Live Activities?
Are there specific security restrictions for Live Activity backgrounds that don't apply to widgets?
Is this behavior intentional based on Apple's design guidelines for Live Activities?
What's the recommended approach for custom backgrounds in Live Activities?
Apple Documentation Reference
I found this guidance in "10 questions with the Live Activities team":
"The Dynamic Island is most immersive when you don't provide background color or imagery — think of it purely as a canvas of foreground view elements."
However, this seems to be design guidance rather than a technical restriction, and it doesn't specifically address Lock Screen Live Activities.
Expected Behavior
Custom user images should display as backgrounds in Live Activities, similar to how they work in widgets.
Current Workaround
I've implemented a color extraction system that generates gradients based on the dominant colors of user images, but users specifically want to see their actual images.
Has anyone successfully implemented custom user images as Live Activity backgrounds, or can Apple clarify the intended behavior and limitations?
Thank you for any insights!
I know for start events, sending the timestamp isn't required. So that's easy.
However when I'm testing Live Activity using the Apple Push Notification Console. A lot of times I just need to copy/paste the payload and resend it again.
But then the timestamp field needs to get updated each time, because if it's from a time in the past, then it won't trigger.
This requires me to have to use an EPOCH converter to find the right time and then copy/paste it.
Is there a better solution to this? I know I can use curl, but that is not in the scope of my question.