Issue starting Live Activities with ActivityKit push notifications

Hi all,

I'm trying to implement starting Live Activities with push notifications according to this article:

https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications

I'm using Xcode 15.1 beta 3, I have run my tests on a physical device with iOS 17.2 as well as the simulator with iOS 17.2

My problem is I can't seem to be able to get the pushToStartToken needed to start the live activities. I have subscribed to the pushToStartTokenUpdates but I never get any updates.

Here is the code I used:

        Task {
            do {
                for try await data in Activity<DailyGoalActivityAttributes>.pushToStartTokenUpdates {
                    let token = data.map {String(format: "%02x", $0)}.joined()
                    print("Activity token: \(token)")
                }
            } catch {
                print(error)
            }

        }

Any help would be greatly appreciated.

Thanks,

HS

After downloading Xcode 15.1 RC I can get it to work on my device (updated with the latest beta), still not working in the simulator.

The only way I found to get the token to update a live activity created through a push notification was to put the following code in my AppDelegate application didFinishLaunchingWithOptions method. I'm able to update a live activity created using push notifications using the token from pushTokenUpdates.

But there is a major design flow IMO, since you can start a live activity without your app being running the following code will never get hit in that instance, and without a way to retrieve the update token from the server side there will be no way of updating the live activity...

I must be missing something but the doc is not helpful in its current state.

Task {
            //
            // Wait for newly created live activities
            //
            for await activityData in Activity<DailyGoalActivityAttributes>.activityUpdates {
                Task {
                    //
                    // Wait for the token needed to update a live activity using push notifications.
                    //
                    for await tokenData in activityData.pushTokenUpdates {
                        let token = tokenData.map {String(format: "%02x", $0)}.joined()
                        print("Push token: \(token)")
                    }
                }
            }
        }

Regarding the payload, here is a working example:

curl -v \
  --header "authorization: bearer ${AUTHENTICATION_TOKEN}" \
  --header "apns-topic: com.yourcompany.app.ios.push-type.liveactivity" \
  --header "apns-push-type: liveactivity" \
  --header "apns-priority: 10" \
  --header "apns-expiration: 0" \
  --data '{"aps":{"timestamp":'$(date +%s)', "event":"start", "content-state": {"currentValue":40000,"targetValue":50000}, "attributes-type":"MyActivityAttributes","attributes":{"currentValue":40000,"targetValue":50000},"alert":{"title":"Almost there!","body":"You have reached 75% of your goal!"}}}' \
  --http2  https://api.development.push.apple.com:443/3/device/${PUSH_TO_START_TOKEN}

My observations have been: the new api (from iOS 17.2) Activity<ActivityAttributes>.pushToStartTokenUpdates returns the pushToStartToken is when Activity<ActivityAttributes>.request has been made (when app is in foreground).

This blog talks about the sequence but it doesn't seem to be working as designed. https://apnspush.com/how-to-start-and-update-live-activities-with-push-notifications

From my experience, it seems that pushToStartTokenUpdates, and similarly pushToStartToken, will only trigger on a fresh app install + having rebooted your device + calling this method in didFinishLaunchingWithOptions, and it has to be that specific combination all at once. That means:

  1. Uninstalling and reinstalling the app will not trigger this token again.
  2. Simply restarting your device without reinstalling the app will also not trigger this token.
  3. If you call this stream in didFinishLaunchingWithOptions, you'll never get the token again unless steps 1 & 2 are also taken first.

This doesn't seem like ideal behavior, as there may be a situation where my application may not be ready to process that push-to-start token. Basically, if my user misses that window to retrieve and process the token, they'll basically never be able to receive a Live Activity. I imagine this is a bug and hopefully gets resolved sooner rather than later.

I realise I’m piling on here, but this thread seems to be the only place on the Internet where this functionality is being discussed.

Where there are static attributes of your ActivityAttributes class, how are you reflecting those in the push to start payload? The example provided by Apple is a little confusing as it has the same model for content-state and attributes.

Could anybody provide insight on how exactly the Live Activity is created via the push notification. Apple's docs state "When the system receives the ActivityKit push notification on a device, it starts a new Live Activity, wakes up your app, and grants it background run time to allow you to download assets that the Live Activity needs." Can anybody elaborate on how/where exactly this happens?

I'm able to successfully obtain the pushToStartToken and can successfully deliver APNS payload via CloudKit - Push Notifications tool. I see in the delivery logs that my push notification was both received by the APNS server and successfully delivered to target device. Once delivered, I see my pushToStartTokenUpdates trigger once more and can get an updated token but, I don't fully understand how/where the live activity is actually created. Any information would be much appreciated

I was experiencing this same issue. To resolve, I added this to my info.plist file. Supports Live Activities: YES

After doing this, I immediately started receiving the token on the next call.

In my experience working on this feature, you only get the push start token once, and you don't receive it more until that token is used to start a live activity.

After the token is used (either from the BE side, or you can use CloudKit to send a test push notification) to start a live activity, you will get a new push token via the pushToStartTokenUpdates.

Does anyone know whether there is a minimum device requirement for ActivityKit.pushTokenUpdates? I'm getting tokens fine on newer devices but not for older ones (i.e. iPhone XR, iPad 8th Generation)

hey @hs-dev are you able to successfully integrate it ?

You can debug much of this using Console.app. The behavior is not entirely consistent across OS versions but nonetheless you can debug to see if your process has lost budget or something else leading to your token not being sent.

A good way to start would be with filtering by the BundleID of the target and / or the target name itself (the process name).

Also be sure to check the system processes for additional information. This will vary based on what and where you are looking but some examples:
To debug APNS you'll want to monitor apnd along with other relevant processes.
To debug WidgetKit, LiveActivities, Dynamic Island you'll want to monitor springboardd, liveactivitiesd along with other relevant processes.

As you monitor processes related to your app and extension you may find additional information.

I would also test with a version of the app deployed to TestFlight as that environment is the same as the AppStore, the debugger loads with a development profile and is not exactly the same, particularly with notification delivery.

Hopefully this helps.

Rico

WWDR - DTS - Software Engineer

Issue starting Live Activities with ActivityKit push notifications
 
 
Q