Skip to main content

LA start: two triggers

A Live Activity can be started by two paths. Both converge on the same token registration flow.

Trigger 1: User follows a live match

Trigger 2: Orchestrator detects match going live

MatchOrchestrator runs a 30-second polling loop. It detects matches transitioning to live status and handles startup recovery — if the backend restarts while matches are live, the Orchestrator picks them up on the next cycle and creates any missing PENDING records.

LA update

Once a Live Activity is active (has a registered update token), it receives score updates through the event-driven pipeline.
Every follower of the same match gets the identical payload. The UnifiedDisplayState is built once per match, not per user. See Live score pipeline for the full detection and payload construction flow.

LA end: four reasons

A Live Activity can end for four distinct reasons. Each has a different trigger path.

1. Match ends

2. User unfollows

3. User dismisses LA on device

Dismissing the LA on-device triggers an unfollow. This is intentional — the LA is the primary UI for a followed live match. If the user dismisses it, the backend treats that as “I don’t want updates for this match.”

4. Token becomes invalid

Token invalidation is self-healing. The Orchestrator’s 30-second loop detects followers who should have an active LA but don’t, and re-sends push-to-start. This handles device restarts, OS-terminated activities, and token rotation.

Token management

Three token types

Token rotation

iOS can rotate the updateToken for an active Live Activity at any time. The flow:
  1. iOS fires pushTokenUpdates on the Activity object
  2. TokenManager observes the new token, deduplicates (skips if same as current)
  3. Posts to POST /matches/:matchId/activity-token with the new token
  4. Backend updates the live-activities record in-place (same PK/SK, new token value)
If the token POST fails (network blip), the backend has a stale token. The next push attempt will get a 410 from APNs, triggering the token-invalid recovery flow. There is retry logic in TokenManager, but a persistent network outage will eventually lead to re-push-to-start via the Orchestrator.

Multi-device support

A single user can have active LAs on multiple devices simultaneously. The live-activities table uses userId#deviceId as the sort key, so each device gets its own record. When the LACoordinator fans out pushes, it sends to all active tokens for a match — regardless of how many belong to the same user.

iOS service architecture

The old monolithic LiveActivityService has been split into three focused managers:

TokenManager

Handles all token observation and registration.
  • Observes pushToStartToken changes on app launch
  • Observes pushTokenUpdates for each active Live Activity
  • Deduplicates tokens (skips POST if token hasn’t changed)
  • Retries failed token registrations

LifecycleManager

Manages the Activity object lifecycle.
  • Calls Activity.request() to create new LAs
  • Restores existing activities on app relaunch (checks ActivityKit state)
  • Runs a stale check: if no update received in 30 minutes, ends the LA locally
  • Adopts push-started activities (activities created via push-to-start that need local tracking)

FollowManager

Manages the follow/unfollow user intent and local state.
  • Calls POST /user/follow/match and DELETE /user/follow/match
  • Persists followed match IDs in UserDefaults for instant UI state
  • Retries pending unfollows on next app launch (handles offline unfollow)
  • Drives the bell icon states:

DynamoDB schema

live-activities table

active-matches table