Updating LiveActivities with Push notifications

1.2k Views Asked by At

I am trying to update the live activities with push notifications and I use the two methods one to start the live activity and the other to update the live activity which are as follows:

func startActivity(completion: @escaping (String) -> Void) {
        print("startActivity() is running")
        let attributes = PizzaDeliveryAttributes(numberOfPizzas: 2, totalAmount: "420", orderNumber: "359")

        let future = Calendar.current.date(byAdding: .minute, value: 35, to: Date())!.addingTimeInterval(40)
        let date = Date.now...future

        let initialContentState = PizzaDeliveryAttributes.ContentState(driverName: "Bill James", deliveryTimer:date)

        let activityContent = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .minute, value: 30, to: Date())!)

        do {
            self.deliveryActivity = try Activity.request(attributes: attributes, content: activityContent, pushType: .token)
            print("Requested a pizza delivery Live Activity \(String(describing: self.deliveryActivity?.id)).")

            Task {
                for await data in self.deliveryActivity!.pushTokenUpdates {
                    let myToken = data.map {String(format: "%02x", $0)}.joined()
                    print("Token printed: \(myToken)")
                    completion(myToken)
                }
            }

        } catch (let error) {
            print("Error requesting pizza delivery Live Activity \(error.localizedDescription).")
        }
    }

    func updatePizzaDeliveryStatus(minutes: String, seconds: String) async {
        var future = Calendar.current.date(byAdding: .minute, value: (Int(minutes) ?? 0), to: Date())!
        future = Calendar.current.date(byAdding: .second, value: (Int(seconds) ?? 0), to: future)!
        let date = Date.now...future
        let updatedDeliveryStatus = PizzaDeliveryAttributes.PizzaDeliveryStatus(driverName: "Anne Johnson", deliveryTimer: date)
        let alertConfiguration = AlertConfiguration(title: "Delivery update", body: "Your pizza order will arrive in 25 minutes.", sound: .default)
        let updatedContent = ActivityContent(state: updatedDeliveryStatus, staleDate: nil)

        do {
            try await self.deliveryActivity?.update(updatedContent, alertConfiguration: alertConfiguration)
        } catch {
            print("Failed to update Live Activity: \(error)")
        }
    }

I am able to generate pushTokens on every new live activity started and also send the curl command through to update the live activity which I will provide below. I expect the dynamic content of the attributes to update, but nothing happens when the push goes through.

curl -v \
--header "apns-topic:<Bundle-Id>.push-type.liveactivity" \
--header "apns-push-type:liveactivity" \
--header "authorization: bearer $AUTHENTICATION_TOKEN" \
--data \
'{"aps": {
   "timestamp":1663300480,
   "event": "update",
   "content-state": {
      "driverName": "Tony Stark",
      "deliveryTimer": {
        "start": 1663300480,       
        "end": 1663301480         
    }
   },
   "alert": {
      "title": "Race Update",
      "body": "Tony Stark is now leading the race!"
   }
}}' \
--http2 \
https://${APNS_HOST_NAME}/3/device/$DEVICE_TOKEN

And this is what content state in the attributes look like:

 public struct ContentState: Codable, Hashable {
            var driverName: String
            var deliveryTimer: ClosedRange<Date>
        }

Any help or insight is deeply appreciated.

2

There are 2 best solutions below

4
On BEST ANSWER

When you run your curl, if you're getting a status code 200 response, then the push notifications are making it to the device.

Assuming you're getting a 200, I can see it being two things.

  1. Issue with stale timestamps
  2. Issue with ContentState serializability

Timestamp

Your timestamp needs to be updated each time you send a push (can't send 2 pushes with the same timestamp). And the time has to be current. You can get that here.

ContentState

iOS is using codable to deserialize the push notification content-state into the struct ContentState. For this to work, the keys must match exactly for both. (I know this ContentState is from their example, but they use a simpler one for the push example)

ClosedRange is codable, but I don't know what the keys are (possibly not start and end).

Try putting only primitive types in your ContentState and see if that fixes it. If so, you can investigate the codability of ClosedRange. Let me know how that goes!

   "content-state": {
      "driverName": "Tony Stark",
      "deliveryStartInt": 1663300480,
      "deliveryEndInt": 1663301480,   
    }
   },
 public struct ContentState: Codable, Hashable {
            var driverName: String
            var deliveryStartInt: Int
            var deliveryEndInt: Int
        }
0
On

Ok so I figured it out.

What I did not realise is the timestamp key in the apns payload, i.e;

"timestamp":1663300480

Needs to be updated every time you send a payload. You can get the latest time stamps from here. Hope this helps!