ActivityKit

RSS for tag

Help 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

112 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Live Acitivities - ProgressView problem
Hello i ve implemented progressview and updating the state via push notification. the progress view wants closedrange which i followed but whenever i get update, the progress value resets to beginning my range is like : Date.now..endDate but i dont get it, lets assume that i get the date from database and initialized already then how the code will understand that what progress value will be as current ? it has to be something like i suppose : startDate..Date.now..endDate thanks
0
0
77
1d
Updating Widget from App Intent called by Live Activity is inconsistent
(Also have a case ID, 9879068) We have an app that user use to check in/out from work for example. We have a button in-app do do this. Now I'm trying to add buttons to our widgets and our new live activity so that users don't have to open the app. It's crucial that the live activity and widgets always show the exact same state. Otherwise it'll look pretty bad if a user has both a live activity and a widget showin at the same time. However, we have noticed that sometimes, pressing the button in the live activity, running the app intent, will not always make the widget update (we call reloadAllTimelines()). The other way around, i.e. press the button on widget to update live activity always works. (they both call the same app intent) When running it in debug mode on a phone from Xcode, it always works, but when running it just on the phone it's unreliable. My first thought was, of course, that's related to the widget "budget", but according to the docs HERE, it should not be applied when interacting with a widget, calling an app intent. My question: HOW can I make my widget reliably refresh using an app intent invoked from a live activity?? I have a ready small project with simple buttons and trace labels that display this issue that I'm happy to supply to someone.
5
0
198
3d
frequentPushEnablementUpdates for live activity is never called.
frequentPushEnablementUpdates asynchronous sequence is never called even if 'More Frequent Updates' is toggled ON or OFF. for await frequentPushEnabled in ActivityAuthorizationInfo().frequentPushEnablementUpdates { // never called } Though we are able to get the 'More Frequent Updates' value once by the following: var isEnabled = ActivityAuthorizationInfo().frequentPushesEnabled //true if ON, false if OFF This only gives the result once as it is not async observation sequence. But the 'frequentPushEnablementUpdates' async sequence is never called. As per the doc - 'frequentPushEnablementUpdates' is an asynchronous sequence you use to observe whether a person permitted you to update Live Activities with frequent ActivityKit push notifications.
0
0
81
2w
Does Live Activity always receive updates for pushToStartTokenUpdates and activityUpdates?
Our context involves smart kitchen appliances, where cooking may be initiated by an app or directly by the device. When the app is not running, we can only start a Live Activity through a remote push notification. However, an increasing number of users report issues where they cannot update or terminate the Live Activity. While we can reproduce this issue in some cases, it is inconsistent and lacks a clear pattern. I have a sample project and would like to confirm the following questions: When the app is not running, does each pushToStartToken update wake the app and reliably trigger the callback below? for await pushToken in Activity<DeviceAttributes>.pushToStartTokenUpdates { } When the app is not running, does each pushTokenUpdates update wake the app and reliably trigger the callback below? Task { for await activity in Activity<DeviceAttributes>.activityUpdates { Task { for try await tokenData in activity.pushTokenUpdates { } } } } Must pushToStartTokenUpdates and pushTokenUpdates be placed directly in application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?), or can they be in another wrapper, such as an RxSwift wrapper? If pushTokenUpdates is updated, but the received pushToken fails to synchronize to the server due to network issues, how should this be handled? Alternatively, if you have any better suggestions, I would be very grateful to hear them. Here is a simple example.
1
0
78
2w
BadDeviceToken Error in Live Activities
Hello everyone, I’m currently receiving feedback from clients in a production environment who are encountering a BadDeviceToken error with Live Activities, which is preventing their states from updating. However, for other clients, the token is working fine and everything functions as expected. I’m collaborating with the back-end developers to gather more information about this issue, but the only log message we’re seeing is: Failed to send a push, APNS reported an error: BadDeviceToken I would greatly appreciate it if anyone could provide some insight or information on how to resolve this issue.
2
0
151
Oct ’24
Sample ActivityConfiguration for Apple Watch?
I'm unable to find sample code that demonstrates how to support a custom Live Activity layout for Apple Watch. I have read the documentation and have added the supplementalActivityFamilies with small and medium. However, I am not able to detect when the activityFamily is set to small. This is what I'm trying to use without success: struct MyWidgetLiveActivity: Widget { @Environment(\.activityFamily) var activityFamily: ActivityFamily var body: some WidgetConfiguration { ActivityConfiguration(for: MyWidgetAttributes.self) { context in if activityFamily == .small { Text("Apple Watch! \(activityFamily.description)") } else { Text("Not small family: \(activityFamily.description)") } } dynamicIsland: { context in return DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text("Leading") } DynamicIslandExpandedRegion(.trailing) { Text("Trailing") } DynamicIslandExpandedRegion(.bottom) { Text("Bottom") } } compactLeading: { Text("CL") } compactTrailing: { Text("CT") } minimal: { Text("M") } } .supplementalActivityFamilies([.small, .medium]) } } This code shows "Not small family: medium" on my Apple Watch. Could somebody provide some insight into why this doesn't work for me?
2
0
169
4w
How to detect interaction of the widget's toggle at locked device
https://developer.apple.com/documentation/WidgetKit/Adding-interactivity-to-widgets-and-Live-Activities#Add-a-toggle Before explaining the situation, I referred to this document. I'm making a widget with a toggle that works with Intent. (To be precise, it's Live Activity, but Apple's literally toggling and button interaction are implemented in the same way) If you press the toggle when the device is locked, the toggle represents the state. However, as described at the top of the same document, the device must be unlocked to execute Intent, so I can't actually change the data. That's fine, but how can I return the isOn of the toggle to false? I'm blocked here because no code is executed. If it is a widget in the home screen, it will be basically unlocked, so I can proceed according to the method of the document, but if it is today's view, user can access it even if device is locked. I tested it in today's view with the Reminders app, If I don't unlock the device after tapping the toggle it returns the toggle back to false state. I wonder how I can achieve this. Is there an API that I missed? Summary. Tap the toggle of the widget while the device is locked. The device waits for unlocking, but the user cancels it without unlocking it. (Still locked) Return the isOn of the toggle to false. <- The part I want.
2
0
184
3w
Decoding Error when starting a Live Activity via Push Notification
Hi, I'm a bit stuck when it comes to implementing live activities. I'm trying to start one by sending a push. my activity attributes looks like: import Foundation import ActivityKit public struct NeoPrototypeActivity: ActivityAttributes { public let classId: String public let name: String public let room: String public let startTime: Date public init(classId: String, name: String, room: String, startTime: Date) { self.classId = classId self.name = name self.room = room self.startTime = startTime } public struct ContentState: Codable & Hashable { public let participationState: ParticipationState public init(participationState: ParticipationState) { self.participationState = participationState } } } public enum ParticipationState: String, Codable, Hashable, Equatable { case upcoming case upcomingOnWaitingList case checkedIn case waitingList case lostSpot case lostSpotFromWaitingList case classCompleted public var description: String { switch self { case .upcoming: return "Upcoming" case .upcomingOnWaitingList: return "Upcoming on Waiting List" case .checkedIn: return "Checked In" case .waitingList: return "Waiting List" case .lostSpot: return "Lost Spot" case .lostSpotFromWaitingList: return "Lost Spot from Waiting List" case .classCompleted: return "Class completed" } } } which I have in a SPM package that is imported by my widget and iOS targets. Then I am sending a push notification (to my simulator in this case) with the following payload { "aps": { "timestamp": 1728419211, "event": "start", "content-state": { "participationState": "upcoming" }, "attributes-type": "NeoPrototypeActivity", "attributes": { "classId": "1234", "name": "Indoor Running", "room": "room 2", "startTime": "2024-10-19T13:22:59+02:00" }, "alert": { "title": "Hola Mundo", "body": "Todo Bien" } } } I am using the right values for the deviceID when sending the push. I get a 200 ok from the CloudKit console. yet my live activity doesn't start. I'm trying to look at the errors in the Console app and the only relevant thing is see is: [NeoPrototypeActivity] Error creating activity: NSCocoaErrorDomain (4864) The data couldn’t be read because it isn’t in the correct format. It seems like a decoding error, but I don't know what exactly failed I wrote some test to try the decoding like: import Testing import Foundation @testable import PrototypeActivities @Test func decoding() async throws { struct StateContainer: Codable { let participationState: ParticipationState } let jsonString = """ { "participationState": "upcoming" } """ let json = try #require(jsonString.data(using: .utf8)) let result = try JSONDecoder().decode(StateContainer.self, from: json) #expect(result.participationState == .upcoming) } @Test func decodingActivity() throws { // let jsonString = """ // { // "classId": "1234", // "name": "Indoor Running", // "room": "room 2", // "startTime": "2024-10-19T11:22:59Z" // } // """ let jsonString = """ { "classId": "1234", "name": "Indoor Running", "room": "room 2", "startTime": 1729337784 } """ let json = try #require(jsonString.data(using: .utf8)) let jsonDecoder = JSONDecoder() // jsonDecoder.dateDecodingStrategy = .iso8601 let activity = try jsonDecoder.decode(NeoPrototypeActivity.self, from: json) #expect(activity.classId == "1234") #expect(activity.name == "Indoor Running") }``` but even with using the default JSON decoder date format, I still get the same error. Not sure how to fix this or to get more details.
2
0
249
Oct ’24
Using a custom DiscreteFormatStyle in Live Activities
With iOS 18, Text has a new initializer that takes in a TimeDataSource and a DiscreteFormatStyle. Similar to this question, I'd like to make a compact timer but can't find a way to do that with any of the system formats. Since the new API takes in a DiscreteFormatStyle though I figure I could make my own. This works in an app but not in a Live Activity; in Previews it crashes and in the simulator the view looks like placeholder. Are custom format styles not supported in this case? Or might there something wrong with my implementation? Formatter Preview Crash Log
1
1
222
Oct ’24
Live Activity expanded leading/trailing regions VoiceOver focus misalignment
There seems to be a misalignment when using VoiceOver and selecting the leading/trailing regions in the expanded presentation for Live Activities. This is demonstrated with an extremely basic sample project, where the expanded region is defined by the following code: DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text("Leading") } DynamicIslandExpandedRegion(.trailing) { Text("Trailing") } DynamicIslandExpandedRegion(.bottom) { Text("Bottom \(context.state.emoji)") } } Is this simply a bug or am I doing something incorrectly/is there a workaround?
2
0
192
Oct ’24
Can I fix this big difference between swiftui preview and actual watch widget layout?
Hi, working on customising my live activity Smart Stack layout for ios18. A thing that is very frustrating is that I consistently looks different for me in the Xcode preview and on the actual watch. See attached screenshots below. The sizes are different, and italic doesn't work on the watch, for example. It makes it time-consuming and unpredictable, so I was wondering if this is a known issue or if I'm doing something wrong, and also can I do anything? thanks edit: this is the layout: var body: some View { VStack(alignment: .center, spacing:4) { HStack(alignment: .center) { IconView(resource: "n-compact-w", bgColor: Color.checkedIn, padding: 2, paddingRight: 6, paddingBottom: 6) .frame(maxWidth: 25, maxHeight: 25).aspectRatio(1, contentMode: .fit) Text("Checked Out") .font(.title3).bold() } Text(status.loc) .font(.headline) .multilineTextAlignment(.center) Text(FormatUtils.getFormattedDateTime(status.time)).font(.subheadline) .multilineTextAlignment(.center).italic() } }
4
0
262
Oct ’24
live activity push channel management request return "TopicMismatch" when bundle in include capital letter
When I request channel list for bundle id "com.apnspush.LiveActivityPushDemo", the request returns "TopicMismatch". Then when I request channel list for bundle id "com.apnspush.liveactivitypush", the request return is normal. I have tried all my bundle ids that include capital letters, they all failed with "TopicMismatch". Does anyone know the reason? curl -v -X GET \ -H "authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg" \ -H "apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127" \ --http2 \ https://api-manage-broadcast.sandbox.push.apple.com:2195/1/apps/com.apnspush.LiveActivityPushDemo/all-channels Note: Unnecessary use of -X or --request, GET is already inferred. * Host api-manage-broadcast.sandbox.push.apple.com:2195 was resolved. * IPv6: (none) * IPv4: 17.138.176.4 * Trying 17.138.176.4:2195... * Connected to api-manage-broadcast.sandbox.push.apple.com (17.138.176.4) port 2195 * ALPN: curl offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: none * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Request CERT (13): * (304) (IN), TLS handshake, Certificate (11): * (304) (IN), TLS handshake, CERT verify (15): * (304) (IN), TLS handshake, Finished (20): * (304) (OUT), TLS handshake, Certificate (11): * (304) (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF * ALPN: server accepted h2 * Server certificate: * subject: C=US; ST=California; O=Apple Inc.; CN=api-manage-broadcast.sandbox.push.apple.com * start date: May 30 17:31:41 2024 GMT * expire date: Apr 10 00:00:00 2025 GMT * subjectAltName: host "api-manage-broadcast.sandbox.push.apple.com" matched cert's "api-manage-broadcast.sandbox.push.apple.com" * issuer: CN=Apple Public Server RSA CA 12 - G1; O=Apple Inc.; ST=California; C=US * SSL certificate verify ok. * using HTTP/2 * [HTTP/2] [1] OPENED stream for https://api-manage-broadcast.sandbox.push.apple.com:2195/1/apps/com.apnspush.LiveActivityPushDemo/all-channels * [HTTP/2] [1] [:method: GET] * [HTTP/2] [1] [:scheme: https] * [HTTP/2] [1] [:authority: api-manage-broadcast.sandbox.push.apple.com:2195] * [HTTP/2] [1] [:path: /1/apps/com.apnspush.LiveActivityPushDemo/all-channels] * [HTTP/2] [1] [user-agent: curl/8.7.1] * [HTTP/2] [1] [accept: */*] * [HTTP/2] [1] [authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg] * [HTTP/2] [1] [apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127] > GET /1/apps/com.apnspush.LiveActivityPushDemo/all-channels HTTP/2 > Host: api-manage-broadcast.sandbox.push.apple.com:2195 > User-Agent: curl/8.7.1 > Accept: */* > authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg > apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127 > * Request completely sent off < HTTP/2 403 < apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127 < * Connection #0 to host api-manage-broadcast.sandbox.push.apple.com left intact {"reason":"TopicMismatch"}% curl -v -X GET \ -H "authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg" \ -H "apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127" \ --http2 \ https://api-manage-broadcast.sandbox.push.apple.com:2195/1/apps/com.apnspush.liveactivitypush/all-channels Note: Unnecessary use of -X or --request, GET is already inferred. * Host api-manage-broadcast.sandbox.push.apple.com:2195 was resolved. * IPv6: (none) * IPv4: 17.138.176.4 * Trying 17.138.176.4:2195... * Connected to api-manage-broadcast.sandbox.push.apple.com (17.138.176.4) port 2195 * ALPN: curl offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: none * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Request CERT (13): * (304) (IN), TLS handshake, Certificate (11): * (304) (IN), TLS handshake, CERT verify (15): * (304) (IN), TLS handshake, Finished (20): * (304) (OUT), TLS handshake, Certificate (11): * (304) (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF * ALPN: server accepted h2 * Server certificate: * subject: C=US; ST=California; O=Apple Inc.; CN=api-manage-broadcast.sandbox.push.apple.com * start date: May 30 17:31:41 2024 GMT * expire date: Apr 10 00:00:00 2025 GMT * subjectAltName: host "api-manage-broadcast.sandbox.push.apple.com" matched cert's "api-manage-broadcast.sandbox.push.apple.com" * issuer: CN=Apple Public Server RSA CA 12 - G1; O=Apple Inc.; ST=California; C=US * SSL certificate verify ok. * using HTTP/2 * [HTTP/2] [1] OPENED stream for https://api-manage-broadcast.sandbox.push.apple.com:2195/1/apps/com.apnspush.liveactivitypush/all-channels * [HTTP/2] [1] [:method: GET] * [HTTP/2] [1] [:scheme: https] * [HTTP/2] [1] [:authority: api-manage-broadcast.sandbox.push.apple.com:2195] * [HTTP/2] [1] [:path: /1/apps/com.apnspush.liveactivitypush/all-channels] * [HTTP/2] [1] [user-agent: curl/8.7.1] * [HTTP/2] [1] [accept: */*] * [HTTP/2] [1] [authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg] * [HTTP/2] [1] [apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127] > GET /1/apps/com.apnspush.liveactivitypush/all-channels HTTP/2 > Host: api-manage-broadcast.sandbox.push.apple.com:2195 > User-Agent: curl/8.7.1 > Accept: */* > authorization: bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkdZSDM5WEZMREEifQ.eyJpYXQiOjE3Mjg3MDM4MDksImlzcyI6IjNXSkdFRjI4R1kifQ.wruX6J4qaovq2X-ZlO7g0YyMWjt50g8YoMoZ4G9ZsLDI5wC8u7PFTaG05BmDvbEzLpzrK9ifwPeo5BJ2eZ3hTg > apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127 > * Request completely sent off < HTTP/2 200 < apns-request-id: 2288cf3f-70d8-46a6-97d7-dd5d00867127 < * Connection #0 to host api-manage-broadcast.sandbox.push.apple.com left intact {"channels":["vtVPwIhLEe8AAG79CdMNuQ=="]}%
0
0
187
Oct ’24
Get Cache error with ActivityKit when reboot App。
We are currently experiencing the following weird issue with our iPhone app. As the title says, NSUserDefaults is losing our custom keys and values when phone is rebooted but not unlocked, and this is happening on a very specific scenario with ActivityKit. Context: We are using the NSUserDefaults in the app to store user data (e.g. username). Issue: An error occurred with no permission to access cached messages after a restart. Scenario: When receiving a Dynamic Island notification, if the phone is restarted, after unlocking the phone and tapping on the Dynamic Island to open the App, all cached information results in an error. Reasons for the error: After restarting the APP, the files are in a locked state. The Dynamic Island proactively invokes the App method. When executing the startup method and retrieving cached messages, an error occurs due to lack of permission. All storage, including files, NSUserDefaults, Keychain, and Plist retrieval, results in errors. The error message is as follows: { "errorCode": "-25308", "errorDesc": "Error Domain=com.samsoffes.sskeychain Code=-25308 "(null)"", "serviceName": "com.qunar.qunarclient8", "account": "iid" } The data returned at this time is in a protected state, [UIApplication sharedApplication].isProtectedDataAvailable. Any help or idea will be truly appreciated :)
0
0
139
Oct ’24