> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cross-platform Live Activity SDK setup

> Add iOS Live Activities to a React Native, Flutter, Unity, Cordova, Capacitor, or .NET MAUI app. The setupDefault method lets the OneSignal SDK own the ActivityKit lifecycle, so the only native code you write is the widget layout.

Follow this guide to add [Live Activities](./live-activities) to an app built with a cross-platform framework such as React Native, Flutter, Unity, Cordova, Capacitor, or .NET MAUI. Live Activities normally require ActivityKit code written in Swift. The OneSignal SDK's `setupDefault` method manages the Live Activity lifecycle for you against a built-in `DefaultLiveActivityAttributes` type, so the only native code you write is the widget layout.

<Note>
  Live Activities are an iOS feature. They are not available on Android, on Huawei devices, or on the web. For a comparable Android experience, see [Android Live Updates](./android-live-notifications).
</Note>

## Requirements

* Your framework's OneSignal SDK, on a release that bundles **OneSignal iOS SDK 5.2.0 or higher**. That version added `setupDefault` and push-to-start (for example, `react-native-onesignal` 5.2.0). Follow [Mobile SDK setup](./mobile-sdk-setup) first.
* OneSignal iOS SDK **5.2.15 or higher** for click tracking and confirmed receipt. See [Live Activities analytics](./live-activities-analytics).
* iOS 16.1+ or iPadOS 17+ to start a Live Activity in-app with `startDefault`.
* iOS 17.2+ to start a Live Activity remotely with the [Start Live Activity API](/reference/start-live-activity). Apple added push-to-start in iOS 17.2, so devices below it can only start activities in-app.
* A [.p8 APNs key](./ios-p8-token-based-connection-to-apns). Apple does not support p12 certificates with Live Activities.
* Xcode 14 or higher.

## Setup

### 1. Set up the OneSignal SDK

Set up the OneSignal SDK for your framework and initialize it in your app before continuing.

<Columns cols={3}>
  <Card title="React Native" icon="react" href="./react-native-sdk-setup">
    Bare React Native apps.
  </Card>

  <Card title="Expo" icon="react" href="./react-native-expo-sdk-setup">
    Managed Expo apps with EAS Build.
  </Card>

  <Card title="Flutter" icon="mobile" href="./flutter-sdk-setup">
    Flutter apps using Dart.
  </Card>

  <Card title="Unity" icon="unity" href="./unity-sdk-setup">
    Unity apps for iOS, Android, Amazon, and Huawei.
  </Card>

  <Card title=".NET MAUI" icon="microsoft" href="./net-sdk-setup">
    .NET MAUI apps for iOS and Android.
  </Card>

  <Card title="Cordova" icon="code" href="./cordova-sdk-setup">
    Cordova and Ionic Cordova hybrid apps.
  </Card>

  <Card title="Capacitor" icon="bolt" href="./capacitor-sdk-setup">
    Capacitor and Ionic Capacitor hybrid apps.
  </Card>
</Columns>

### 2. Call `setupDefault` on app launch

`setupDefault` tells the OneSignal SDK to manage the Live Activity lifecycle for its built-in `DefaultLiveActivityAttributes` type. Call it once per app launch, right after you initialize the OneSignal SDK. The SDK then registers the device's push-to-start token, which is what lets you start, update, and end the activity with the [Start Live Activity](/reference/start-live-activity) and [Update Live Activity](/reference/update-live-activity-api) APIs.

To start an activity from inside your app instead of remotely, call `startDefault` with an `activityId`, static attributes, and initial content. Your app must be in the foreground.

<CodeGroup>
  ```javascript React Native/Expo theme={null}
  import { Platform } from 'react-native';
  import { OneSignal } from 'react-native-onesignal';

  // Live Activities are iOS-only, so guard the call.
  if (Platform.OS === 'ios') {
    // Register for push-to-start. Run this on every app launch.
    OneSignal.LiveActivities.setupDefault();

    // Optional: start the activity from inside the app instead of with a push.
    const activityId = 'my_activity_id';
    const attributes = { title: 'Sample Title' };
    const content = { message: { en: 'message' } };
    OneSignal.LiveActivities.startDefault(activityId, attributes, content);
  }
  ```

  ```dart Flutter theme={null}
  import 'package:onesignal_flutter/onesignal_flutter.dart';

  // Register for push-to-start. Run this on every app launch.
  OneSignal.LiveActivities.setupDefault();

  // Optional: start the activity from inside the app instead of with a push.
  const String activityId = "my_activity_id";
  OneSignal.LiveActivities.startDefault(activityId, {
    "title": "Welcome!",
  }, {
    "message": {"en": "Hello World!"},
  });
  ```

  ```csharp Unity / .NET MAUI theme={null}
  using OneSignalSDK;

  // Register for push-to-start. Run this on every app launch.
  OneSignal.LiveActivities.SetupDefault();

  // Optional: start the activity from inside the app instead of with a push.
  string activityId = "my_activity_id";

  OneSignal.LiveActivities.StartDefault(
    activityId,
    new Dictionary<string, object>() {
        { "title", "Welcome!" }
    },
    new Dictionary<string, object>() {
        { "message", new Dictionary<string, object>() {
            { "en", "Hello World!"}
        }},
    });
  ```

  ```javascript Cordova theme={null}
  // Register for push-to-start. Run this on every app launch.
  window.plugins.OneSignal.LiveActivities.setupDefault();

  // Optional: start the activity from inside the app instead of with a push.
  const activityId = "my_activity_id";
  const attributes = { title: "Sample Title" };
  const content = { message: { en: "message" } };
  window.plugins.OneSignal.LiveActivities.startDefault(
    activityId,
    attributes,
    content
  );
  ```

  ```javascript Capacitor/Ionic theme={null}
  import OneSignal from "onesignal-cordova-plugin";

  // Register for push-to-start. Run this on every app launch.
  OneSignal.LiveActivities.setupDefault();

  // Optional: start the activity from inside the app instead of with a push.
  const activityId = "my_activity_id";
  const attributes = { title: "Sample Title" };
  const content = { message: { en: "message" } };
  OneSignal.LiveActivities.startDefault(activityId, attributes, content);
  ```
</CodeGroup>

<Note>
  Live Activities are iOS-only. The Flutter SDK checks the platform internally, so `setupDefault` does nothing on Android. In React Native, guard the call as shown above.
</Note>

`setupDefault` listens for both push-to-start and push-to-update tokens by default. In React Native and Flutter, pass setup options to enable only one of them. For example, if you always start activities from inside the app, you can skip push-to-start registration.

<CodeGroup>
  ```javascript React Native/Expo theme={null}
  OneSignal.LiveActivities.setupDefault({
    enablePushToStart: false,
    enablePushToUpdate: true,
  });
  ```

  ```dart Flutter theme={null}
  OneSignal.LiveActivities.setupDefault(
    options: LiveActivitySetupOptions(
      enablePushToStart: false,
      enablePushToUpdate: true,
    ),
  );
  ```
</CodeGroup>

### 3. Create the Live Activity widget extension

<Steps>
  <Step title="Update your Info.plist">
    In Xcode, open your main target's `Info.plist`, add the key `Supports Live Activities` as **Boolean**, and set it to `YES`. The raw key name is `NSSupportsLiveActivities`.

    <Frame caption="Add Supports Live Activities key to Info.plist and set its value to Boolean YES.">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/info-la-setting.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=e839231401651b71d0ae8d48614ec130" alt="Xcode Info.plist editor showing Supports Live Activities set to YES" width="2166" height="1488" data-path="images/live-activities/info-la-setting.png" />
    </Frame>

    <Note>
      If your use case sends frequent high-priority updates, also add [`NSSupportsLiveActivitiesFrequentUpdates`](https://developer.apple.com/documentation/bundleresources/information-property-list/nssupportsliveactivitiesfrequentupdates) as a Boolean set to `YES` (iOS 16.2+). Apple throttles apps that send `priority: 10` updates too often, and this key raises that budget. See [Update frequency and throttling](./live-activities#update-frequency-and-throttling).
    </Note>
  </Step>

  <Step title="Create a Widget Extension">
    In Xcode, go to **File > New > Target... > Widget Extension**.

    <Frame caption="Add a new Widget Extension target for your app in Xcode.">
      <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/live-activity-widget-extension.png?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=60a52d6484203c4d6af21bc3a7da5e62" alt="Xcode new target dialog with Widget Extension selected" width="2166" height="1488" data-path="images/live-activities/live-activity-widget-extension.png" />
    </Frame>

    Select and press **Next**.

    Configure the Widget Extension by providing a name (example: `OneSignalWidget`) and ensure **Include Live Activity** is selected. Then click **Finish**.

    <Frame caption="Widget Extension options for a Live Activity.">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/OneSignalWidget.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=47412314963f6fbd58fd2c7d3a529322" alt="Widget Extension configuration with Include Live Activity checkbox selected" width="2166" height="1488" data-path="images/live-activities/OneSignalWidget.png" />
    </Frame>

    Click **Don't Activate** if prompted to activate the scheme.

    <Frame caption="Scheme activation prompt: select Don't Activate.">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/la-scheme-activation.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=d3a2207ff2b5988c63c2abd631a72541" alt="Xcode scheme activation dialog for the Widget Extension" width="2166" height="1488" data-path="images/live-activities/la-scheme-activation.png" />
    </Frame>
  </Step>

  <Step title="Add the OneSignalLiveActivities subspec to your Podfile (optional)">
    This step is only required if your app uses a Live Activity widget extension with CocoaPods.

    Find the name of your widget extension target in your project's Targets list. The example below uses `OneSignalWidgetExtension`.

    <Frame caption="Find the name of your widget extension target.">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/cocoapods-la-extension-name.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=850a91387539587802306a66771435eb" alt="Xcode Targets list showing the widget extension target name" width="2166" height="1488" data-path="images/live-activities/cocoapods-la-extension-name.png" />
    </Frame>

    Open your `Podfile` and add only the `OneSignalXCFramework/OneSignalLiveActivities` subspec. Replace `OneSignalWidgetExtension` with the name of your widget extension target. Do not use the aggregate `pod 'OneSignalXCFramework'` declaration, which resolves `OneSignalComplete` and includes the location module.

    ```ruby Podfile theme={null}
    target 'OneSignalWidgetExtension' do
      #use_frameworks!
      pod 'OneSignalXCFramework/OneSignalLiveActivities', '>= 5.0.0', '< 6.0'
    end
    ```

    Close Xcode and run `pod repo update && pod install` to install the `OneSignalLiveActivities` pod.
  </Step>
</Steps>

### 4. Build the widget layout

In Xcode, open the `WidgetExtensionLiveActivity.swift` file that the Widget Extension template generated.

Open the Inspector panel on the right side of the screen. Within **Target Membership**, click the **+** button and select your main app target. In Flutter this target is named `Runner`. In other frameworks it matches your app name.

<Frame caption="Allow main target membership for the Live Activity file.">
  <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/target-membership.png?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=3315253623dfec6c13a56ff30a156772" alt="Xcode Target Membership section with the main app target selected" width="2392" height="1488" data-path="images/live-activities/target-membership.png" />
</Frame>

Replace the contents of the file with the layout below. Change the displayed values to whatever your activity needs. You do not define an `ActivityAttributes` struct yourself, because `setupDefault` supplies `DefaultLiveActivityAttributes`, which exposes your payload as `context.attributes.data` and `context.state.data`.

```swift Swift theme={null}
import ActivityKit
import WidgetKit
import SwiftUI
import OneSignalLiveActivities

// Your struct name might be different here
@available(iOS 16.2, *)
struct OneSignalWidgetLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: DefaultLiveActivityAttributes.self) { context in
            // Lock screen / banner UI
            VStack {
                Spacer()

                Text("Title: " + (context.attributes.data["title"]?.asString() ?? ""))
                    .font(.headline)

                Spacer()

                HStack {
                    Spacer()
                    Text(context.state.data["message"]?.asDict()?["en"]?.asString() ?? "Default Message")
                    Spacer()
                }

                Text("INT: " + String(context.state.data["intValue"]?.asInt() ?? 0))
                Text("DBL: " + String(context.state.data["doubleValue"]?.asDouble() ?? 0.0))
                Text("BOL: " + String(context.state.data["boolValue"]?.asBool() ?? false))

                Spacer()
            }
            .activitySystemActionForegroundColor(.black)
            .activityBackgroundTint(.white)

        } dynamicIsland: { _ in
            DynamicIsland {
                // Expanded UI
                DynamicIslandExpandedRegion(.leading) {
                    Text("Leading")
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Text("Trailing")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Text("Bottom")
                    // More content
                }
            } compactLeading: {
                Text("L")
            } compactTrailing: {
                Text("T")
            } minimal: {
                Text("Min")
            }
            .widgetURL(URL(string: "http://www.apple.com"))
            .keylineTint(Color.red)
        }
    }
}
```

<Note>
  Keep the `@available(iOS 16.2, *)` annotation from the template. A widget gated this way does not render on iOS 16.1, so treat 16.2 as the practical floor for this setup. Set `widgetURL` to your own deep link, since the example points at `apple.com`.
</Note>

***

## Test the Live Activity

Build and run your app on a device or simulator running iOS 17.2 or higher, then send a start request.

Two fields carry your widget's data, and both mirror the structure your Swift layout reads:

* `event_attributes` holds static data. You set it in the start request and it stays fixed for the life of the activity. Your widget reads it as `context.attributes.data`.
* `event_updates` holds dynamic data you change on later updates. Your widget reads it as `context.state.data`. Nest values exactly as your layout expects, so the `message` dictionary below arrives as `context.state.data["message"]["en"]`.

Before you send, replace these values:

* `YOUR_APP_ID` with your [OneSignal App ID](./keys-and-ids) and `YOUR_REST_API_KEY` with your [API key](./keys-and-ids).
* `activity_id` with an identifier of your choice. A new value starts a new Live Activity, and reusing a value updates the activity already using it. See [Choose an activity ID](/reference/start-live-activity#choose-an-activity-id).

Leave `DefaultLiveActivityAttributes` in the URL path as-is. That is the type `setupDefault` registers, and the path segment is case-sensitive. For every available field, see the [Start Live Activity API reference](/reference/start-live-activity).

```curl curl theme={null}
curl --request POST \
     --url https://api.onesignal.com/apps/YOUR_APP_ID/activities/activity/DefaultLiveActivityAttributes \
     --header 'Authorization: key YOUR_REST_API_KEY' \
     --header 'Content-Type: application/json' \
     --header 'accept: application/json' \
     --data '
{
  "event": "start",
  "event_updates": {
    "data": {
      "message": {
        "en": "The message is nested in context.state.data[\"message\"] as a dictionary"
      },
      "intValue": 10,
      "doubleValue": 3.14,
      "boolValue": true
    }
  },
  "event_attributes": {
    "data": {
      "title": "this is set when the LA starts and does not get updated after"
    }
  },
  "activity_id": "my-activity-id",
  "name": "Live Activity test",
  "headings": {
    "en": "Your order is on the way"
  },
  "contents": {
    "en": "Arriving in 15 minutes"
  },
  "ios_sound": "beep.wav",
  "priority": 10
}'
```

***

## Low-level methods

Use these methods only if you want to define your own `ActivityAttributes` struct in Swift and manage the push-to-start token yourself, instead of letting `setupDefault` own the lifecycle. You generate the token with ActivityKit in your iOS code, then hand it to OneSignal. For the native side, see the [alternative setup notes in the iOS SDK](https://github.com/OneSignal/OneSignal-iOS-SDK/pull/1377).

`activityType` must be the name of the struct conforming to `ActivityAttributes` that starts the activity, and it becomes the last segment of the API URL path.

<CodeGroup>
  ```javascript React Native/Expo theme={null}
  // Setting the push-to-start token
  OneSignal.LiveActivities.setPushToStartToken(activityType, token);

  // Removing the push-to-start token
  OneSignal.LiveActivities.removePushToStartToken(activityType);
  ```

  ```dart Flutter theme={null}
  // Setting the push-to-start token
  OneSignal.LiveActivities.setPushToStartToken(activityType, token);

  // Removing the push-to-start token
  OneSignal.LiveActivities.removePushToStartToken(activityType);
  ```

  ```csharp Unity / .NET MAUI theme={null}
  // Setting the push-to-start token
  OneSignal.LiveActivities.SetPushToStartToken(activityType, token);

  // Removing the push-to-start token
  OneSignal.LiveActivities.RemovePushToStartToken(activityType);
  ```

  ```javascript Cordova theme={null}
  // Setting the push-to-start token
  window.plugins.OneSignal.LiveActivities.setPushToStartToken(activityType, token);

  // Removing the push-to-start token
  window.plugins.OneSignal.LiveActivities.removePushToStartToken(activityType);
  ```

  ```javascript Capacitor/Ionic theme={null}
  // Setting the push-to-start token
  OneSignal.LiveActivities.setPushToStartToken(activityType, token);

  // Removing the push-to-start token
  OneSignal.LiveActivities.removePushToStartToken(activityType);
  ```
</CodeGroup>

***

## FAQ

### Why does my Live Activity never start from a push?

The most common cause is a device below iOS 17.2. Apple added push-to-start in iOS 17.2, so older devices can only start activities in-app with `startDefault`. If the device qualifies, check that `setupDefault` runs on every app launch after `OneSignal.initialize`, that the activity type in your URL path is exactly `DefaultLiveActivityAttributes`, and that your app uses a [.p8 APNs key](./ios-p8-token-based-connection-to-apns) rather than a p12 certificate.

### Do I need to define my own ActivityAttributes struct?

No. `setupDefault` registers the SDK's `DefaultLiveActivityAttributes` type, which is why the widget reads values through `context.attributes.data` and `context.state.data` instead of named properties. Define your own struct only if you use the [low-level methods](#low-level-methods).

### Why is my widget showing default or empty values?

Your payload nesting does not match what the layout reads. `event_attributes` maps to `context.attributes.data` and `event_updates` maps to `context.state.data`, so a value the widget reads as `context.state.data["message"]["en"]` must arrive as `event_updates.data.message.en`. Values that miss the expected path fall back to the defaults in your Swift code.

### Can I use Live Activities on Android?

No. Live Activities are an iOS-only ActivityKit feature, so the calls on this page do nothing on Android. For a comparable Android experience built on push notifications, see [Android Live Updates](./android-live-notifications).

### Does the widget need its own OneSignal pod?

Only if your app uses CocoaPods. In that case add the `OneSignalXCFramework/OneSignalLiveActivities` subspec to your widget extension target, as shown in step 3. Do not add the aggregate `OneSignalXCFramework` pod to the widget target, because it resolves `OneSignalComplete` and pulls in the location module.

***

## Related pages

<Columns cols={2}>
  <Card title="Live Activities" icon="mobile-screen" href="./live-activities">
    When to use Live Activities, targeting, update frequency, and throttling limits.
  </Card>

  <Card title="Live Activities developer setup" icon="apple" href="./live-activities-developer-setup">
    The native iOS (Swift) setup path, including custom `ActivityAttributes`.
  </Card>

  <Card title="Start Live Activity API" icon="code" href="/reference/start-live-activity">
    Every field accepted by the push-to-start request.
  </Card>

  <Card title="Live Activities analytics" icon="chart-line" href="./live-activities-analytics">
    Delivery, confirmed receipt, click, and unsubscribe metrics for each send.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.