UNPKG

@cliffhelsel/capacitor-live-activity

Version:

A Capacitor plugin for managing iOS Live Activities using ActivityKit and Swift.

376 lines (248 loc) 14.4 kB
# 📡 capacitor-live-activity [![npm](https://img.shields.io/npm/v/capacitor-live-activity)](https://www.npmjs.com/package/capacitor-live-activity) [![bundle size](https://img.shields.io/bundlephobia/minzip/capacitor-live-activity)](https://bundlephobia.com/result?p=capacitor-live-activity) [![License: MIT](https://img.shields.io/npm/l/capacitor-live-activity)](./LICENSE) [![Platforms](https://img.shields.io/badge/platforms-iOS-orange)](#-platform-behavior) [![Capacitor](https://img.shields.io/badge/capacitor-7.x-blue)](https://capacitorjs.com/) A Capacitor plugin for managing iOS Live Activities using ActivityKit and Swift. > [!TIP] > 🚀 Looking for a ready-to-run demo? → [Try the Example App](./example-app/) ## 🧭 Table of contents - [🧭 Table of contents](#-table-of-contents) - [📦 Install](#-install) - [🧩 Widget Setup (Required)](#-widget-setup-required) - [1. Add a Widget Extension in Xcode](#1-add-a-widget-extension-in-xcode) - [2. Configure the Widget (Example)](#2-configure-the-widget-example) - [3. Add GenericAttributes.swift to your Widget Target](#3-add-genericattributesswift-to-your-widget-target) - [To make it available in your widget extension:](#to-make-it-available-in-your-widget-extension) - [Why is this needed?](#why-is-this-needed) - [4. Add Capability](#4-add-capability) - [5. Ensure Inclusion in Build](#5-ensure-inclusion-in-build) - [📱 Example App](#-example-app) - [🛠 API](#-api) ## 📦 Install ```bash npm install capacitor-live-activity npx cap sync ``` > [!NOTE] > This plugin requires **iOS 16.2+** to work properly due to `ActivityKit` API usage. > [!IMPORTANT] > This plugin **requires a Live Activity widget extension** to be present and configured in your Xcode project. > Without a widget, Live Activities will not appear on the lock screen or Dynamic Island. ## 🧩 Widget Setup (Required) To use Live Activities, your app must include a widget extension that defines the UI for the Live Activity using ActivityKit. Without this, the Live Activity will not appear on the Lock Screen or Dynamic Island. ### 1. Add a Widget Extension in Xcode 1. Open your app’s iOS project in Xcode. 2. Go to File > New > Target… 3. Choose Widget Extension. 4. Name it e.g. LiveActivityWidget. 5. Check the box “Include Live Activity”. 6. Finish and wait for Xcode to generate the files. ### 2. Configure the Widget (Example) Make sure the widget uses the same attribute type as the plugin (e.g. GenericAttributes.swift): ```swift import ActivityKit import WidgetKit import SwiftUI struct LiveActivityWidgetLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: GenericAttributes.self) { context in // Lock Screen UI VStack { Text(context.state.values["title"] ?? "⏱") Text(context.state.values["status"] ?? "-") } } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text(context.state.values["title"] ?? "") } DynamicIslandExpandedRegion(.trailing) { Text(context.state.values["status"] ?? "") } DynamicIslandExpandedRegion(.bottom) { Text(context.state.values["message"] ?? "") } } compactLeading: { Text("🔔") } compactTrailing: { Text(context.state.values["status"] ?? "") } minimal: { Text("🎯") } } } } ``` ### 3. Add GenericAttributes.swift to your Widget Target To support Live Activities with dynamic values, this plugin uses a shared Swift struct called GenericAttributes. > By default, it’s located under: Pods > CapacitorLiveActivity > LiveActivityPlugin > Shared > GenericAttributes.swift #### To make it available in your widget extension: 1. Open Xcode and go to the File Navigator. 2. Expand Pods > CapacitorLiveActivity > Shared. 3. Copy GenericAttributes.swift to Widget Extension Target, e.g. LiveActivityWidget 4. Make sure to select "Copy files to destination" #### Why is this needed? Xcode doesn’t automatically include files from a CocoaPods plugin into your widget target. Without this step, your widget won’t compile because it cannot find GenericAttributes. ### 4. Add Capability Go to your main app target → Signing & Capabilities tab and add: - Background Modes → Background fetch ### 5. Ensure Inclusion in Build - In your **App target’s Info.plist**, ensure: ```xml <key>NSSupportsLiveActivities</key> <true/> ``` - Clean and rebuild the project (Cmd + Shift + K, then Cmd + B). ## 📱 Example App This plugin includes a fully functional demo app under the [`example-app/`](./example-app) directory. The demo is designed to run on real iOS devices and showcases multiple Live Activity types like delivery, timer, taxi, workout, and more. - Launch and test various Live Activities interactively - Trigger updates and alert banners - View JSON state changes in a live log console > [!NOTE] > For full instructions, see [example-app/README.md](./example-app/README.md) ## 🛠 API <docgen-index> * [`startActivity(...)`](#startactivity) * [`startObservingPushToStartToken()`](#startobservingpushtostarttoken) * [`addListener('pushToStartToken', ...)`](#addlistenerpushtostarttoken-) * [`updateActivity(...)`](#updateactivity) * [`endActivity(...)`](#endactivity) * [`isAvailable()`](#isavailable) * [`isRunning(...)`](#isrunning) * [`getCurrentActivity(...)`](#getcurrentactivity) * [Interfaces](#interfaces) * [Type Aliases](#type-aliases) </docgen-index> <docgen-api> <!--Update the source file JSDoc comments and rerun docgen to update the docs below--> ### startActivity(...) ```typescript startActivity(options: StartActivityOptions) => Promise<void> ``` Starts a new Live Activity on iOS using the provided options. | Param | Type | | ------------- | --------------------------------------------------------------------- | | **`options`** | <code><a href="#startactivityoptions">StartActivityOptions</a></code> | **Since:** 0.0.1 -------------------- ### startObservingPushToStartToken() ```typescript startObservingPushToStartToken() => Promise<void> ``` -------------------- ### addListener('pushToStartToken', ...) ```typescript addListener(eventName: 'pushToStartToken', listenerFunc: (data: { token: string; }) => void) => Promise<PluginListenerHandle> ``` | Param | Type | | ------------------ | -------------------------------------------------- | | **`eventName`** | <code>'pushToStartToken'</code> | | **`listenerFunc`** | <code>(data: { token: string; }) =&gt; void</code> | **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code> -------------------- ### updateActivity(...) ```typescript updateActivity(options: UpdateActivityOptions) => Promise<void> ``` Updates the currently active Live Activity. | Param | Type | | ------------- | ----------------------------------------------------------------------- | | **`options`** | <code><a href="#updateactivityoptions">UpdateActivityOptions</a></code> | **Since:** 0.0.1 -------------------- ### endActivity(...) ```typescript endActivity(options: EndActivityOptions) => Promise<void> ``` Ends the Live Activity and optionally provides a final state and dismissal policy. | Param | Type | | ------------- | ----------------------------------------------------------------- | | **`options`** | <code><a href="#endactivityoptions">EndActivityOptions</a></code> | **Since:** 0.0.1 -------------------- ### isAvailable() ```typescript isAvailable() => Promise<boolean> ``` Returns whether Live Activities are available on this device and allowed by the user. **Returns:** <code>Promise&lt;boolean&gt;</code> **Since:** 0.0.1 -------------------- ### isRunning(...) ```typescript isRunning(options: { id: string; }) => Promise<boolean> ``` Returns true if a Live Activity with the given ID is currently running. | Param | Type | | ------------- | ---------------------------- | | **`options`** | <code>{ id: string; }</code> | **Returns:** <code>Promise&lt;boolean&gt;</code> **Since:** 0.0.1 -------------------- ### getCurrentActivity(...) ```typescript getCurrentActivity(options?: { id?: string | undefined; } | undefined) => Promise<LiveActivityState | undefined> ``` Returns the current active Live Activity state, if any. If an ID is provided, returns that specific activity. If no ID is given, returns the most recently started activity. | Param | Type | | ------------- | ----------------------------- | | **`options`** | <code>{ id?: string; }</code> | **Returns:** <code>Promise&lt;<a href="#liveactivitystate">LiveActivityState</a>&gt;</code> **Since:** 0.0.1 -------------------- ### Interfaces #### StartActivityOptions Options for starting a Live Activity. | Prop | Type | Description | | ------------------ | --------------------------------------------------------------- | --------------------------------------------------------- | | **`id`** | <code>string</code> | Unique ID to identify the Live Activity. | | **`attributes`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Immutable attributes that are part of the Live Activity. | | **`contentState`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Initial content state (dynamic values). | | **`timestamp`** | <code>number</code> | Optional timestamp (Unix) when the Live Activity started. | #### PluginListenerHandle | Prop | Type | | ------------ | ----------------------------------------- | | **`remove`** | <code>() =&gt; Promise&lt;void&gt;</code> | #### UpdateActivityOptions Options for updating a Live Activity. | Prop | Type | Description | | ------------------ | ----------------------------------------------------------------- | -------------------------------------------------------------------------------- | | **`id`** | <code>string</code> | ID of the Live Activity to update. | | **`contentState`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Updated content state (dynamic values). | | **`alert`** | <code><a href="#alertconfiguration">AlertConfiguration</a></code> | Optional alert configuration to show a notification banner or Apple Watch alert. | | **`timestamp`** | <code>number</code> | Optional timestamp (Unix) when the update occurred. | #### AlertConfiguration Configuration for alert notifications. | Prop | Type | Description | | ----------- | ------------------- | -------------------------------------- | | **`title`** | <code>string</code> | Optional title of the alert. | | **`body`** | <code>string</code> | Optional body text of the alert. | | **`sound`** | <code>string</code> | Optional sound file name or "default". | #### EndActivityOptions Options for ending a Live Activity. | Prop | Type | Description | | ------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | **`id`** | <code>string</code> | ID of the Live Activity to end. | | **`contentState`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Final state to show before dismissal. | | **`timestamp`** | <code>number</code> | Optional timestamp (Unix) when the end occurred. | | **`dismissalDate`** | <code>number</code> | Optional dismissal time in the future (Unix). If not provided, system default applies. | #### LiveActivityState Represents an active Live Activity state. | Prop | Type | Description | | --------------- | --------------------------------------------------------------- | ----------------------------------------------- | | **`id`** | <code>string</code> | The unique identifier of the activity. | | **`values`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | The current dynamic values of the activity. | | **`isStale`** | <code>boolean</code> | Whether the activity is stale. | | **`isEnded`** | <code>boolean</code> | Whether the activity has ended. | | **`startedAt`** | <code>string</code> | ISO string timestamp when the activity started. | ### Type Aliases #### Record Construct a type with a set of properties K of type T <code>{ [P in K]: T; }</code> </docgen-api>