Skip to content

Live activity: fix: improve push-to-start safety, backoff, and token-wait behaviour#625

Open
MtlPhil wants to merge 1 commit intoloopandlearn:feat/la-ios17-push-to-startfrom
achkars-org:live-activity
Open

Live activity: fix: improve push-to-start safety, backoff, and token-wait behaviour#625
MtlPhil wants to merge 1 commit intoloopandlearn:feat/la-ios17-push-to-startfrom
achkars-org:live-activity

Conversation

@MtlPhil
Copy link
Copy Markdown

@MtlPhil MtlPhil commented Apr 28, 2026

Summary

Six targeted fixes to the iOS 17.2+ push-to-start implementation, scoped entirely to LiveActivityManager.swift and APNSClient.swift.


1. Keep old LA alive until APNs send confirms success

Previously dispatchPushToStart ended the old activity before sending to APNs. If the send failed (rate-limited, invalid token, network error) the user lost their existing Live Activity with nothing to replace it.

The old activity is now ended only after a .success result from sendLiveActivityStart. On any failure path the existing LA remains visible.


2. Reset backoff to zero on adoption

After a successful send, handlePushToStartResult set laPushToStartBackoff to the 5-minute base. That value was never cleared after activityUpdates confirmed the new LA, so a legitimate renewal arriving within 5 minutes of adoption was silently blocked.

adoptPushToStartActivity now resets laPushToStartBackoff to 0 once the new activity is confirmed.


3. Add apns-collapse-id to push-to-start requests

Without a collapse key, two sends racing (e.g. a background refresh tick and a user-initiated restart overlapping) could create two Live Activities. Added <bundleID>.la.start as the collapse ID so APNs coalesces redundant sends.


4. Set apns-expiration to 10 minutes instead of 0

apns-expiration: 0 means "deliver right now or discard." A momentary connectivity loss at the wrong moment would permanently lose the start notification.

Changed to now + 10 minutes — long enough to survive a brief offline gap while the glucose reading in the payload is still clinically meaningful. The stale date of 8 hours was considered and rejected: delivering a start notification with hours-old glucose data is worse than not starting at all.


5. Raise pushToStartForceRestartThreshold from 2 to 4

The FB21158660 workaround force-restarts the LA when N consecutive successful APNs sends have not been followed by an activityUpdates adoption. A threshold of 2 produced false positives on slow connections where the activityUpdates delivery simply lagged the send confirmation. Raised to 4.


6. Single automatic retry when push-to-start token is not yet available

On first install or after a permission toggle, the push-to-start token may not have arrived before the first LA start attempt. Previously this caused an immediate "could not start" notification after a 5-second poll timeout.

dispatchPushToStart now accepts an isRetry: Bool = false parameter. On first timeout it waits 10 seconds and retries once before surfacing the error to the user. A pushToStartTokenRetryDelay = 10 constant is added alongside the existing poll constants.

- Keep old LA alive until APNs send confirms success, so a failed
  push-to-start (rate-limited, invalid token, network error) no longer
  leaves the user with no activity and nothing to replace it
- Reset laPushToStartBackoff to 0 in adoptPushToStartActivity so a
  near-term renewal is not silently blocked by the 5-minute post-send
  base interval once the new LA is confirmed by activityUpdates
- Add apns-collapse-id (bundle-id.la.start) so APNs coalesces redundant
  push-to-start sends that race (refresh tick + user restart)
- Set apns-expiration to 10 minutes instead of 0 so a brief
  connectivity gap does not permanently lose the start notification,
  while avoiding delivery of clinically stale glucose data
- Raise pushToStartForceRestartThreshold from 2 to 4 to reduce false
  positives on slow connections where activityUpdates delivery lags
- Add a single automatic 10-second retry when the push-to-start token
  is not yet available, before surfacing the "could not start" error

https://claude.ai/code/session_01GJZERMhqLmEy8p4cpVX53q
@bjorkert bjorkert self-requested a review April 28, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants