LiveActivity start via APNs not working

I am trying to issue the "start" APNs push notification to start a live activity for my iOS app. The notification appears to send correctly, there is no error message, but the live activity never appears for any of my users (users are in TestFlight).

In addition to issuing the APNs commands from my server, I have also tried using the CloudKit Push Notification Console to manually generate a "start" notification. It submits correctly but the live activity never starts.

I have also checked the Console app to watch the device logs and see if iOS is rejecting/throttling the live activity but I don't see any activity related to the start message at all.

Here are some details:

App bundle ID: `com.penzu.moodmoji`
APNs topic: `com.penzu.moodmoji.push-type.liveactivity`
APNs push type: `liveactivity`
Recent apns-id: `7b633309-b7fd-4163-b620-776efa04f315`

APNs payload:

{
"aps": {
"timestamp": 1742651625,
"event": "start",
"content-state": {
"totalDays": 7,
"currentDay": 2,
"progress": 0.29,
"status": "ACTIVE",
"reportReady": false
},
"attributes-type": "GoalActivityAttributes",
"attributes": {
"totalDays": 7,
"currentDay": 2,
"progress": 0.29,
"status": "ACTIVE",
"reportReady": false
},
"alert": {
"title": "It's day 2!",
"body": "Don't forget to record every time you feel anxious today."
}
}
}

I can confirm that LiveActivities started by the iOS app with ActivityKit work correctly, and the app does appear to be receiving pushToStartTokenUpdates:

struct GoalActivityAttributes: ActivityAttributes, Sendable {
struct ContentState: Codable & Hashable, Sendable {
let totalDays: Int
let currentDay: Int
let progress: Double
let status: String
let reportReady: Bool
}
let goal: SimpleGoal
}
for await nextStartToken in Activity<GoalActivityAttributes>.pushToStartTokenUpdates {
// send nextStartToken to server...
}

The app I'm testing with is in TestFlight, using the production APNs environment.

Accepted Answer

I have determined the cause of this problem and solved it for myself. There were multiple issues with the format of the start live activity JSON payload.

Problem 1:

Apple's documentation (here) makes it look like content-state and attributes contain the same data structure which maps onto the ActivityAttributes.ContentState. In fact, attributes should be the immutable attributes of your ActivityAttributes struct, and content-state should be the nested .ContentState struct.

This documentation really needs to be corrected! I'm probably not the only one who wasted many hours because of it. Looking at the EmojiRangers source code it's clear that the attributes should contain a single nested hero object, but in the incorrect docs it contains the ContentState structure.

Problem 2:

The JSON that is acceptable to APNS is a subset of all JSON syntax. For example it wasn't working if a null was provided for a value even though my struct allows for it.

Another problem was dates in the JSON must be represented as unix epoch integers (in seconds) rather than ISO date strings.

It required wading through thousands of lines of device console logs across multiple subsystems and categories to figure this out.

LiveActivity start via APNs not working
 
 
Q