Skip to content

Managing Live Activities locally

Live Activities are dynamic interfaces that display real-time information on iOS devices. Voltra provides a comprehensive set of APIs for managing the complete lifecycle of Live Activities directly from your React Native app, without requiring server-side infrastructure.

Voltra offers both imperative APIs for direct control and React hooks for seamless integration with your components.

This page documents the legacy server-rendered engine. For the separate engine that bundles a definition with the app and updates it with props, see Dynamic Live Activities.

The imperative APIs provide direct, programmatic control over Live Activities. These are the core functions you’ll use to manage Live Activity lifecycles. For the shape of the variants object used below, see Supported variants.

Use startLiveActivity() to create and display a new Live Activity.

import { Voltra } from '@use-voltra/ios'
import { startLiveActivity } from '@use-voltra/ios-client'
const variants = {
lockScreen: (
<Voltra.VStack style={{ padding: 16, borderRadius: 18, backgroundColor: '#101828' }}>
<Voltra.Text style={{ color: '#F8FAFC', fontSize: 18, fontWeight: '600' }}>
Order Confirmed
</Voltra.Text>
<Voltra.Text style={{ color: '#94A3B8', fontSize: 12, marginTop: 8 }}>
Your order is being prepared
</Voltra.Text>
</Voltra.VStack>
),
// Define compact, minimal, and expanded variants for Dynamic Island
compact: <Voltra.Text>Order confirmed</Voltra.Text>,
minimal: <Voltra.Symbol name="checkmark.circle.fill" tintColor="#10B981" />,
expanded: (
<Voltra.VStack style={{ padding: 16 }}>
<Voltra.Text style={{ fontSize: 16, fontWeight: '600' }}>Order Confirmed</Voltra.Text>
<Voltra.Text style={{ fontSize: 12, color: '#6B7280', marginTop: 4 }}>
Your order is being prepared
</Voltra.Text>
</Voltra.VStack>
),
}
const activityId = await startLiveActivity(variants, {
activityName: 'order-123', // Optional: for re-binding on app restart
deepLinkUrl: 'myapp://order/123', // Optional: URL to open when tapped
channelId: 'CTrNsYq/Ee8AALLzHQaVlA==', // Optional: broadcast channel (iOS 18+)
dismissalPolicy: { after: 30 },
staleDate: Date.now() + 60 * 60 * 1000, // 1 hour
relevanceScore: 0.8,
})

Parameters:

  • variants: A LiveActivityVariants object defining the UI for different display contexts
  • options: Configuration options (see Configuration Options section below)

Returns: A promise that resolves to the Live Activity ID (string)

Use updateLiveActivity() to modify the content and configuration of an active Live Activity.

import { Voltra } from '@use-voltra/ios'
import { updateLiveActivity } from '@use-voltra/ios-client'
const updatedVariants = {
lockScreen: (
<Voltra.VStack style={{ padding: 16, borderRadius: 18, backgroundColor: '#101828' }}>
<Voltra.Text style={{ color: '#F8FAFC', fontSize: 18, fontWeight: '600' }}>
Order Ready
</Voltra.Text>
<Voltra.Text style={{ color: '#94A3B8', fontSize: 12, marginTop: 8 }}>
Your order is ready for pickup
</Voltra.Text>
<Voltra.Button onPress="pickup-order" style={{ marginTop: 12 }}>
I'm Here
</Voltra.Button>
</Voltra.VStack>
),
compact: <Voltra.Text>Ready for pickup</Voltra.Text>,
minimal: <Voltra.Symbol name="bag.fill" tintColor="#F59E0B" />,
}
await updateLiveActivity(activityId, updatedVariants, {
dismissalPolicy: { after: 300 }, // 5 minutes
relevanceScore: 1.0, // High priority now
})

Parameters:

  • activityId: The ID returned from startLiveActivity()
  • variants: Updated UI variants
  • options: Updated configuration options

Use stopLiveActivity() to end a Live Activity.

import { stopLiveActivity } from '@use-voltra/ios-client'
await stopLiveActivity(activityId, {
dismissalPolicy: { after: 10 }, // Keep visible for 10 seconds after ending
})

Parameters:

  • activityId: The ID of the Live Activity to stop
  • options: Final configuration options

Use isLiveActivityActive() to check if a specific Live Activity is currently active.

import { isLiveActivityActive } from '@use-voltra/ios-client'
if (isLiveActivityActive('order-123')) {
console.log('Live Activity is active')
} else {
console.log('Live Activity is not active')
}

Parameters:

  • activityName: The name of the Live Activity to check (same as activityName used when starting the activity)

Returns: Boolean indicating whether the Live Activity is currently active

import { isGlassSupported } from '@use-voltra/ios-client'
function App() {
// Check if the device supports Liquid Glass (iOS 26+)
if (isGlassSupported()) {
// Use Liquid Glass features
}
}

Voltra also exposes useIsHeadless() for detecting when your app was launched in the background (e.g. to handle a push token refresh without mounting your UI). See Handling background execution for the full explanation and example.

Use endAllLiveActivities() to immediately end all active Live Activities in your app.

import { endAllLiveActivities } from '@use-voltra/ios-client'
// End all Live Activities (useful for cleanup or logout scenarios)
await endAllLiveActivities()

Note: This function ends all Live Activities immediately without applying any dismissal policies. Use this for bulk cleanup scenarios rather than individual activity management.

For development-friendly APIs that provide automatic updates and easier testing, see the Development documentation.

Live Activities emit events that you can listen to for state changes and user interactions. See the Events API documentation for detailed information on subscribing to and handling Live Activity events.

Voltra provides several configuration options to control Live Activity behavior, lifecycle, and appearance. These options can be used with startLiveActivity(), updateLiveActivity(), and stopLiveActivity().

Controls how Live Activities behave after they end.

Options:

  • 'immediate' (default): Live Activity is dismissed immediately when it ends
  • { after: number }: Live Activity remains visible for the specified number of seconds after ending, then automatically dismisses

Examples:

// Immediate dismissal (default)
await startLiveActivity(variants, {
dismissalPolicy: 'immediate',
})
// Keep visible for 30 seconds after ending
await startLiveActivity(variants, {
dismissalPolicy: { after: 30 },
})
// Update dismissal timing for active Live Activities
await updateLiveActivity(activityId, variants, {
dismissalPolicy: { after: 300 }, // 5 minutes
})
// Set dismissal timing when stopping
await stopLiveActivity(activityId, {
dismissalPolicy: { after: 10 },
})

The dismissal policy applies to both programmatic ending and natural ending (when timers expire).

Specifies when a Live Activity should be considered stale and automatically dismissed by iOS.

// Dismiss after 1 hour
await startLiveActivity(variants, {
staleDate: Date.now() + 60 * 60 * 1000,
})
// Dismiss after 2 hours
await startLiveActivity(variants, {
staleDate: Date.now() + 2 * 60 * 60 * 1000,
})

Note: If you provide a staleDate in the past, it will be ignored.

Helps iOS prioritize which Live Activities to display when space is limited.

Range: 0.0 to 1.0 (default: 0.0)

// High priority (e.g., active delivery)
await startLiveActivity(variants, {
relevanceScore: 0.8,
})
// Low priority (e.g., background task)
await startLiveActivity(variants, {
relevanceScore: 0.2,
})

Live Activities can receive updates even when your app is in the background or terminated, but they cannot execute JavaScript code. For real-time updates from backgrounded apps, use server-side push notifications.

Subscribes the Live Activity to a broadcast channel for server-side updates. When provided, the activity receives updates via broadcast push notifications instead of individual device tokens—one server notification updates all activities on that channel.

// For shared content (e.g., live sports, flight status)
await startLiveActivity(variants, {
activityName: 'match-123',
channelId: 'CTrNsYq/Ee8AALLzHQaVlA==', // From APNs channel management
})

Requires enablePushNotifications: true in the Voltra plugin and the Broadcast Capability enabled in your Apple Developer account. See Server-side updates for details.

  • Provide an activityName when starting a Live Activity, so you can re-bind to it (e.g. via isLiveActivityActive) after an app restart.
  • Set a staleDate so iOS can automatically dismiss activities you forget to end (e.g. after a delivery is likely already complete).
  • Provide a deepLinkUrl so tapping the Live Activity takes users somewhere useful in your app.
  • Pick a dismissalPolicy deliberately — { after: N } is useful when you want users to see a final state (like “Delivered”) before it disappears.