{"_id":"@ciabosoftwaresolutions/capacitor-live-activities","_rev":"3-a59c6a355ba1102c0c9bc87ffffc12c0","name":"@ciabosoftwaresolutions/capacitor-live-activities","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@ciabosoftwaresolutions/capacitor-live-activities","version":"0.1.0","keywords":["capacitor","plugin","live-activities","live-updates","dynamic-island","ios","android","notifications"],"author":{"name":"Ciabo Software Solutions"},"license":"MIT","_id":"@ciabosoftwaresolutions/capacitor-live-activities@0.1.0","maintainers":[{"name":"ciabosoftwaresolutions","email":"ciabosoftwaresolutions@gmail.com"}],"homepage":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities#readme","bugs":{"url":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities/issues"},"dist":{"shasum":"107cdf72f1b3cd8ef46222a16efe8d9b9ddd74a9","tarball":"https://registry.npmjs.org/@ciabosoftwaresolutions/capacitor-live-activities/-/capacitor-live-activities-0.1.0.tgz","fileCount":25,"integrity":"sha512-Hu+erbP6kXcC+2y5JAK7DI0toUzcW+kHqbgh27e7CVTve18JERRXhHkt2yolJA9NdhejVpvk617VLVPz/0EtFQ==","signatures":[{"sig":"MEUCIQCoOJHz+JhgfvEHUnw4K6IdFHStcRLRXDA+LKRxxqoPUwIgQjdYieOf3X3r6n0qCywWBIwrlcAFv7N9Pk+qPU9Qi1Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":105713},"main":"dist/plugin.cjs.js","type":"module","types":"dist/esm/index.d.ts","unpkg":"dist/plugin.js","module":"dist/esm/index.js","gitHead":"e70e222be4aa8282eeff9ab6147224fd1ee7290a","scripts":{"fmt":"prettier \"**/*.{css,html,ts,js,java}\" --write","lint":"eslint . --ext ts","build":"npm run clean && npm run docgen && tsc && rollup -c rollup.config.js","clean":"rimraf ./dist","docgen":"docgen --api LiveActivitiesPlugin --project tsconfig.docgen.json --output-readme README.md --output-json dist/docs.json","verify":"npm run verify:ios && npm run verify:android && npm run verify:web","verify:ios":"cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS build | xcpretty","verify:web":"npm run build","verify:android":"cd android && ./gradlew clean build test"},"_npmUser":{"name":"ciabosoftwaresolutions","email":"ciabosoftwaresolutions@gmail.com"},"prettier":"@ionic/prettier-config","capacitor":{"ios":{"src":"ios"},"android":{"src":"android"}},"swiftlint":"@ionic/swiftlint-config","repository":{"url":"git+https://github.com/ciabosoftwaresolutions/capacitor-live-activities.git","type":"git"},"_npmVersion":"11.12.1","description":"Capacitor plugin for iOS Live Activities (Dynamic Island + Lock Screen) and Android Live Updates notifications","directories":{},"_nodeVersion":"25.9.0","eslintConfig":{"extends":"@ionic/eslint-config/recommended"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","rimraf":"^6.0.1","rollup":"^4.21.3","prettier":"^3.3.3","typescript":"^5.5.4","@capacitor/ios":"^8.4.0","@capacitor/core":"^8.4.0","@capacitor/docgen":"^0.3.1","@capacitor/android":"^8.4.0","@ionic/eslint-config":"^0.4.0","prettier-plugin-java":"^2.6.4","@ionic/prettier-config":"^4.0.0","@ionic/swiftlint-config":"^1.1.2"},"peerDependencies":{"@capacitor/core":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/capacitor-live-activities_0.1.0_1780543134755_0.20580981921536545","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@ciabosoftwaresolutions/capacitor-live-activities","version":"0.1.1","keywords":["capacitor","plugin","live-activities","live-updates","dynamic-island","ios","android","notifications"],"author":{"name":"Ciabo Software Solutions"},"license":"MIT","_id":"@ciabosoftwaresolutions/capacitor-live-activities@0.1.1","maintainers":[{"name":"ciabosoftwaresolutions","email":"ciabosoftwaresolutions@gmail.com"}],"homepage":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities#readme","bugs":{"url":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities/issues"},"dist":{"shasum":"86057c499e89abbef943198bab3868bcf6e59c00","tarball":"https://registry.npmjs.org/@ciabosoftwaresolutions/capacitor-live-activities/-/capacitor-live-activities-0.1.1.tgz","fileCount":25,"integrity":"sha512-/2XgrTrL5bRmClyMr5fA7EMcQW/E3tYo3ntysVj36d4/ooaaqVC2Ja3oUvnxihy0K8NjM3eVMtJUCrkSq+GeMg==","signatures":[{"sig":"MEUCIQDjtb+KdadYJoIHIlLHJS65nH3dUfyBrDDtbVqfVZjWdQIgagNFK+GOyBzKNh74cigXXS0JSNmCR9xTNMouedaB7Ks=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ciabosoftwaresolutions%2fcapacitor-live-activities@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":105713},"main":"dist/plugin.cjs.js","type":"module","types":"dist/esm/index.d.ts","unpkg":"dist/plugin.js","module":"dist/esm/index.js","gitHead":"765de3b8296915915d05c70a7a02a80f91d69b57","scripts":{"fmt":"prettier \"**/*.{css,html,ts,js,java}\" --write","lint":"eslint . --ext ts","build":"npm run clean && npm run docgen && tsc && rollup -c rollup.config.js","clean":"rimraf ./dist","docgen":"docgen --api LiveActivitiesPlugin --project tsconfig.docgen.json --output-readme README.md --output-json dist/docs.json","verify":"npm run verify:ios && npm run verify:android && npm run verify:web","verify:ios":"cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS build | xcpretty","verify:web":"npm run build","verify:android":"cd android && ./gradlew clean build test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5f2d3866-599a-470e-a596-8671cf8a6847"}},"prettier":"@ionic/prettier-config","capacitor":{"ios":{"src":"ios"},"android":{"src":"android"}},"swiftlint":"@ionic/swiftlint-config","repository":{"url":"git+https://github.com/ciabosoftwaresolutions/capacitor-live-activities.git","type":"git"},"_npmVersion":"11.13.0","description":"Capacitor plugin for iOS Live Activities (Dynamic Island + Lock Screen) and Android Live Updates notifications","directories":{},"_nodeVersion":"24.16.0","eslintConfig":{"extends":"@ionic/eslint-config/recommended"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","rimraf":"^6.0.1","rollup":"^4.21.3","prettier":"^3.3.3","typescript":"^5.5.4","@capacitor/ios":"^8.4.0","@capacitor/core":"^8.4.0","@capacitor/docgen":"^0.3.1","@capacitor/android":"^8.4.0","@ionic/eslint-config":"^0.4.0","prettier-plugin-java":"^2.6.4","@ionic/prettier-config":"^4.0.0","@ionic/swiftlint-config":"^1.1.2"},"peerDependencies":{"@capacitor/core":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/capacitor-live-activities_0.1.1_1780546152129_0.1953131860476558","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@ciabosoftwaresolutions/capacitor-live-activities","version":"0.1.2","description":"Capacitor plugin for iOS Live Activities (Dynamic Island + Lock Screen) and Android Live Updates notifications","main":"dist/plugin.cjs.js","module":"dist/esm/index.js","types":"dist/esm/index.d.ts","unpkg":"dist/plugin.js","type":"module","author":{"name":"Ciabo Software Solutions"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/ciabosoftwaresolutions/capacitor-live-activities.git"},"bugs":{"url":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities/issues"},"homepage":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities#readme","keywords":["capacitor","plugin","live-activities","live-updates","dynamic-island","ios","android","notifications"],"scripts":{"build":"npm run clean && npm run docgen && tsc && rollup -c rollup.config.js","clean":"rimraf ./dist","docgen":"docgen --api LiveActivitiesPlugin --project tsconfig.docgen.json --output-readme README.md --output-json dist/docs.json","lint":"eslint . --ext ts","fmt":"prettier \"**/*.{css,html,ts,js,java}\" --write","verify":"npm run verify:ios && npm run verify:android && npm run verify:web","verify:ios":"cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS build | xcpretty","verify:android":"cd android && ./gradlew clean build test","verify:web":"npm run build"},"devDependencies":{"@capacitor/android":"^8.4.0","@capacitor/core":"^8.4.0","@capacitor/docgen":"^0.3.1","@capacitor/ios":"^8.4.0","@ionic/eslint-config":"^0.4.0","@ionic/prettier-config":"^4.0.0","@ionic/swiftlint-config":"^1.1.2","eslint":"^8.57.0","prettier":"^3.3.3","prettier-plugin-java":"^2.6.4","rimraf":"^6.0.1","rollup":"^4.21.3","typescript":"^5.5.4"},"peerDependencies":{"@capacitor/core":"^8.0.0"},"capacitor":{"ios":{"src":"ios"},"android":{"src":"android"}},"swiftlint":"@ionic/swiftlint-config","prettier":"@ionic/prettier-config","eslintConfig":{"extends":"@ionic/eslint-config/recommended"},"gitHead":"c545f1acf5566d722f638015cae415147146959b","_id":"@ciabosoftwaresolutions/capacitor-live-activities@0.1.2","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-lyJ6vZYw7CyjTnwp0iqVFK3n+w++S7jP4eIiBMvc8N4FiOQXVL/wUhs2DEqhKSc6moniB0dV8f5dsw5QSjt57g==","shasum":"74f4cdaeabe1e519d7cdc05001e8b21dfc628886","tarball":"https://registry.npmjs.org/@ciabosoftwaresolutions/capacitor-live-activities/-/capacitor-live-activities-0.1.2.tgz","fileCount":25,"unpackedSize":147976,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ciabosoftwaresolutions%2fcapacitor-live-activities@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCwhqEuo/hL9WzKoBAIQhjtuBB20gLFdJT1VGLXDuxKiwIgDely796a7PoZxretyiVkDqa6aRqF077hdMMnstJgaBI="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5f2d3866-599a-470e-a596-8671cf8a6847"}},"directories":{},"maintainers":[{"name":"ciabosoftwaresolutions","email":"ciabosoftwaresolutions@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/capacitor-live-activities_0.1.2_1780721266391_0.8756281978855371"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-04T03:18:54.584Z","modified":"2026-06-06T04:47:46.823Z","0.1.0":"2026-06-04T03:18:54.921Z","0.1.1":"2026-06-04T04:09:12.366Z","0.1.2":"2026-06-06T04:47:46.526Z"},"bugs":{"url":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities/issues"},"author":{"name":"Ciabo Software Solutions"},"license":"MIT","homepage":"https://github.com/ciabosoftwaresolutions/capacitor-live-activities#readme","keywords":["capacitor","plugin","live-activities","live-updates","dynamic-island","ios","android","notifications"],"repository":{"type":"git","url":"git+https://github.com/ciabosoftwaresolutions/capacitor-live-activities.git"},"description":"Capacitor plugin for iOS Live Activities (Dynamic Island + Lock Screen) and Android Live Updates notifications","maintainers":[{"name":"ciabosoftwaresolutions","email":"ciabosoftwaresolutions@gmail.com"}],"readme":"# @ciabosoftwaresolutions/capacitor-live-activities\n\nCapacitor plugin for **iOS Live Activities** (Dynamic Island + Lock Screen) and **Android Live Updates** (persistent status-bar chip, Android 16+, with a sticky notification fallback on Android 13–15).\n\n[![npm version](https://img.shields.io/npm/v/@ciabosoftwaresolutions/capacitor-live-activities)](https://www.npmjs.com/package/@ciabosoftwaresolutions/capacitor-live-activities)\n[![npm downloads](https://img.shields.io/npm/dm/@ciabosoftwaresolutions/capacitor-live-activities)](https://www.npmjs.com/package/@ciabosoftwaresolutions/capacitor-live-activities)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nIf this plugin saves you time, consider buying us a coffee ☕\n\n[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-yellow?logo=buy-me-a-coffee&logoColor=white)](https://buymeacoffee.com/ciabosoftwaresolutions)\n\n---\n\n## Platform support\n\n| Feature | iOS | Android |\n|---|---|---|\n| Compiles from | ✅ iOS 13+ | ✅ API 23+ |\n| Live Activity / Live Update | ✅ iOS 16.2+ (runtime) | ✅ Android 16+ (API 36) |\n| Graceful fallback on older OS | ✅ `isSupported()` → false | ✅ Sticky notification |\n| Dynamic Island | ✅ iPhone 14 Pro+ | — |\n| Lock Screen banner | ✅ | — |\n| Status-bar chip | — | ✅ |\n| App-driven updates | ✅ | ✅ |\n| Push-driven updates (server → device) | ✅ APNs — no Firebase needed | ✅ FCM — optional, see below |\n\n---\n\n## How updates work — app-driven vs push-driven\n\nThere are two ways to keep a Live Activity in sync. **You can use either or both.**\n\n### App-driven (default — no Firebase, no APNs setup)\n\nYour app calls `LiveActivities.update()` directly, for example from a WebSocket handler or a background fetch. Works on all supported OS versions with zero extra setup.\n\n```\nYour app  ──update()──▶  Native layer  ──▶  Live Activity UI\n```\n\n### Push-driven (server sends updates while app is closed)\n\nYour server pushes a payload directly to the device. The OS updates the Live Activity UI even if your app is not running.\n\n```\nYour server  ──POST──▶  APNs / FCM  ──▶  Live Activity UI\n```\n\n| | iOS | Android |\n|---|---|---|\n| Protocol | APNs `liveActivity` push type | FCM data message |\n| Firebase needed? | ❌ No — direct APNs | ✅ Yes (optional peer dep) |\n| Token source | `getPushToken({ activityId })` | `getPushToken({ activityId })` |\n\n---\n\n## Requirements\n\n| Tool | Minimum version |\n|---|---|\n| Capacitor | 8.0 |\n| iOS deployment target | 13.0 (Live Activities activate at runtime on 16.2+) |\n| Android `minSdkVersion` | 23 |\n| Android `compileSdkVersion` | 35+ |\n| Java | 21 |\n| Node | 18+ |\n\n## Installation\n\n```bash\nnpm install @ciabosoftwaresolutions/capacitor-live-activities\nnpx cap sync\n```\n\n---\n\n## iOS setup\n\n> **Device requirement** — Live Activities and the Dynamic Island render on physical devices only. The Lock Screen banner is visible in the iOS 16.2+ Simulator but the Dynamic Island is not. Always test the full experience on a real device.\n\n---\n\n### Compatibility — iOS 13+ compiles, iOS 16.2+ activates\n\nThe plugin and Widget Extension template both use `#if canImport(ActivityKit)` and `@available(iOS 16.2, *)` guards throughout, so **they compile against any deployment target from iOS 13 upwards**. No code changes are needed in your project.\n\nAt runtime the behaviour is:\n\n| iOS version | `isSupported()` | `start()` / `update()` / `end()` |\n|---|---|---|\n| iOS 16.2+ | `true` | Works normally |\n| iOS 13 – 16.1 | `false` | Rejects with a clear error message |\n\nAlways check `isSupported()` before calling `start()` and branch accordingly in your app logic.\n\n```typescript\nconst { supported } = await LiveActivities.isSupported();\nif (supported) {\n  const { activityId } = await LiveActivities.start({ ... });\n} else {\n  // Fall back to a regular notification, badge, or nothing\n}\n```\n\n---\n\n### Step 1 — Minimum deployment target (optional adjustment)\n\nThe plugin works with whatever deployment target your project already has. If you **want** to raise it to iOS 16.2 to remove the availability checks from your own Swift/Capacitor code, set it in Xcode → select target → **General** → **Minimum Deployments** → `iOS 16.2`. This is entirely optional.\n\n---\n\n### Step 2 — Enable capabilities on the main app target\n\nIn Xcode → select your **main app target** → **Signing & Capabilities**:\n\n1. Click **+ Capability** → add **Live Activities** (this also adds WidgetKit automatically).\n2. Click **+ Capability** → add **Push Notifications** *(required even for app-driven updates — the OS needs this entitlement to issue APNs activity tokens)*.\n3. Click **+ Capability** → add **Background Modes** → check **Remote notifications** *(allows the app to receive push-driven updates while in the background)*.\n\n---\n\n### Step 3 — Update Info.plist\n\nAdd these keys to your main app's `Info.plist`:\n\n```xml\n<!-- Required: opt the app in to Live Activities -->\n<key>NSSupportsLiveActivities</key>\n<true/>\n\n<!-- Optional but recommended: allow updates more than once per hour.\n     Without this key iOS throttles updates aggressively. -->\n<key>NSSupportsLiveActivitiesFrequentUpdates</key>\n<true/>\n```\n\n---\n\n### Step 4 — Add a Widget Extension target\n\nThe Live Activity UI (Lock Screen banner + Dynamic Island) runs inside a separate Widget Extension mini-app that Apple's system spawns independently.\n\n1. **File → New → Target → Widget Extension** — name it e.g. `LiveActivityWidget`.\n2. When prompted, **uncheck** *\"Include Configuration App Intent\"* and **check** *\"Include Live Activity\"*.\n3. Xcode creates the extension with a new bundle ID — by convention use `<your-main-bundle-id>.LiveActivityWidget` (e.g. `com.yourcompany.yourapp.LiveActivityWidget`). Verify this under the extension target → **General → Bundle Identifier**.\n4. Set the extension's **Minimum Deployments** to `iOS 16.2` (same as Step 1).\n5. Replace the generated Swift file with the ready-made template from this repo:\n   ```\n   example/ios/LiveActivityWidget/LiveActivityWidget.swift\n   ```\n6. Add `LiveActivityManager.swift` (from `ios/Plugin/` in this repo) to **both** the main app target **and** the Widget Extension target. This is what lets the extension read the same `LiveActivityAttributes` type that the plugin writes.\n   - Select `LiveActivityManager.swift` in the Xcode file navigator.\n   - Open the **File Inspector** (right panel).\n   - Under **Target Membership** check both your main app target and `LiveActivityWidget`.\n\n---\n\n### Step 5 — Add a shared App Group\n\nAn App Group lets the main app and the widget extension share data across the process boundary.\n\n1. Select your **main app target** → **Signing & Capabilities** → **+ Capability** → **App Groups**.\n2. Click **+** and add a group: `group.com.yourcompany.yourapp`\n3. Select the **LiveActivityWidget target** → repeat the same steps with the **exact same** group identifier.\n\n> Xcode generates a `.entitlements` file for each target automatically when you add a capability. If you already have an entitlements file, the capability is merged into it.\n\n---\n\n### Step 6 — Register the plugin (Capacitor 6)\n\nThe plugin auto-registers via `@CapacitorPlugin` — no manual Swift code needed. Just run:\n\n```bash\nnpx cap sync\n```\n\n---\n\n### Step 7 — APNs key (push-driven updates only)\n\nSkip this step if you are only using app-driven updates.\n\nTo send server-side Live Activity updates via APNs you need an **APNs Auth Key** (`.p8` file) — this is a single key that works for all your apps, unlike the older certificate-based approach.\n\n1. Go to [Apple Developer → Certificates, Identifiers & Profiles → Keys](https://developer.apple.com/account/resources/authkeys/list).\n2. Click **+** → name it (e.g. `APNs Auth Key`) → check **Apple Push Notifications service (APNs)** → **Continue → Register → Download**.\n3. Save the `.p8` file securely — you can only download it once.\n4. Note your:\n   - **Key ID** (shown on the key detail page, 10-character string)\n   - **Team ID** (top-right of the developer portal, also 10 characters)\n   - **Bundle ID** of your app\n\nYour server uses these three values plus the `.p8` file to sign JWT tokens for APNs requests. See the [Push-driven updates — iOS](#ios--apns-no-firebase-required) section below for the payload format.\n\n---\n\n## Android setup\n\n### 1. Request notification permission at runtime (Android 13+)\n\n```typescript\nimport { Capacitor } from '@capacitor/core';\n\nif (Capacitor.getPlatform() === 'android') {\n  // Use your preferred permission library or the Capacitor Permissions API\n  await Notification.requestPermission();\n}\n```\n\n### 2. compileSdkVersion\n\nTo use the Android 16 Live Updates chip (`FLAG_LIVE_UPDATE`), set `compileSdkVersion = 36` in your app's `android/variables.gradle`. The plugin falls back to a standard sticky notification automatically on older versions.\n\n---\n\n## Usage\n\n```typescript\nimport { LiveActivities } from '@ciabosoftwaresolutions/capacitor-live-activities';\n\n// Check support\nconst { supported } = await LiveActivities.isSupported();\nconst { enabled }   = await LiveActivities.areActivitiesEnabled();\n\n// Start\nconst { activityId } = await LiveActivities.start({\n  attributes: {\n    activityType: 'order-tracking',   // identifies the activity kind\n    orderId: '12345',                 // static: never changes during the activity\n  },\n  state: {\n    title: 'Order on its way',        // required\n    subtitle: '3 stops away',\n    progress: 0.6,                    // 0.0 – 1.0\n    icon: 'shippingbox.fill',         // SF Symbol (iOS) / drawable name (Android)\n  },\n  staleAfterSeconds: 3600,            // iOS only\n});\n\n// Update (app-driven)\n// ⚠️ update() REPLACES the entire state — re-send every field you want to keep\n// (icon, colors, etc.), not just the ones that changed. See the callout below.\nawait LiveActivities.update({\n  activityId,\n  state: {\n    title: 'Order arriving now',\n    subtitle: 'Next stop',\n    progress: 0.95,\n    icon: 'shippingbox.fill',   // re-send, or it disappears\n  },\n  alertTitle: 'Your order is almost here!',   // iOS: shows a banner\n  alertBody: '1 stop away',\n});\n\n// End\nawait LiveActivities.end({\n  activityId,\n  finalState: {\n    title: 'Delivered!',\n    subtitle: 'Enjoy your order',\n    progress: 1.0,\n    icon: 'checkmark.circle.fill',\n  },\n  dismissalPolicy: 'after-delay',   // iOS only; leaves it visible briefly\n});\n\n// Get active activities\nconst { activities } = await LiveActivities.getActiveActivities();\n\n// Listen for system-driven state changes (iOS only)\nawait LiveActivities.addListener('activityStateChanged', (event) => {\n  console.log(event.activityId, event.activityState); // 'active' | 'ended' | 'dismissed'\n});\n```\n\n> ### ⚠️ `update()` and `end()` replace the **entire** state\n>\n> Live Activities are stateless between updates — each `update()` (and `end()`'s\n> `finalState`) **fully replaces** the previous content state. Any field you omit\n> reverts to its default: a missing `icon` disappears, missing `progressColor`\n> falls back to white, a missing `subtitle` vanishes.\n>\n> **Always re-send every field you want to keep**, not just the ones that changed:\n>\n> ```typescript\n> // Keep one source of truth for the visual style, then spread it on every call\n> const style = {\n>   icon: 'shippingbox.fill',\n>   backgroundColor: '#1a1a2e',\n>   progressColor: '#4ade80',\n>   textColor: '#ffffff',\n> };\n>\n> await LiveActivities.start({ attributes, state: { title: 'Order placed', progress: 0, ...style } });\n> await LiveActivities.update({ activityId, state: { title: 'On the way', progress: 0.6, ...style } });\n> await LiveActivities.end({ activityId, finalState: { title: 'Delivered', progress: 1, ...style } });\n> ```\n\n---\n\n## Push-driven updates\n\n### iOS — APNs (no Firebase required)\n\nCall `getPushToken()` right after `start()` and send the token to your server. The token is specific to each activity instance and may rotate — always use the latest one from the `pushTokenUpdated` event.\n\n```typescript\n// Get token immediately after start\nconst { token, type } = await LiveActivities.getPushToken({ activityId });\n// type === 'apns'\nawait yourServer.registerActivityToken({ activityId, token });\n\n// Re-register whenever the token rotates (iOS may reissue it)\nawait LiveActivities.addListener('pushTokenUpdated', async (event) => {\n  await yourServer.registerActivityToken({\n    activityId: event.activityId,\n    token: event.token,  // always use the freshest token\n  });\n});\n```\n\n**Server payload** — send a `POST` to `https://api.push.apple.com/3/device/<token>` with:\n\n```json\n{\n  \"aps\": {\n    \"timestamp\": 1700000000,\n    \"event\": \"update\",\n    \"content-state\": {\n      \"title\": \"Order arriving now\",\n      \"subtitle\": \"1 stop away\",\n      \"progress\": 0.95\n    },\n    \"alert\": {\n      \"title\": \"Order update\",\n      \"body\": \"Your order is almost here!\"\n    }\n  }\n}\n```\n\nThe `content-state` keys map directly to `LiveActivityState` fields. Use `\"event\": \"end\"` to end the activity from the server.\n\n> **APNs push type must be** `liveactivity` and the `:path` header must be `/3/device/<activityPushToken>` (not the regular device token).\n\n#### ⚠️ Caveat — don't use `timerEnd` / `timerStart` over push\n\nThose two fields map to Swift `Date`. When ActivityKit decodes a pushed `content-state`, it uses `Codable`'s default date strategy — **seconds since 2001-01-01**, *not* Unix epoch. So a Unix timestamp sent from your server lands ~31 years off.\n\nFor **push-driven** timers, send a numeric `progress` (0–1) and a text subtitle (e.g. `\"Arriving by 8:45 PM\"`) instead, and recompute on the server with each push. App-driven timers (set on-device via `start()`/`update()`) are unaffected — use `timerEnd` freely there.\n\n---\n\n### iOS — push-to-start (launch activities from the server, iOS 17.2+)\n\n`getPushToken()` requires the activity to already be running on-device. To **start** a Live Activity entirely from your server — perfect for order tracking or appointment reminders where the app may be closed — use the **push-to-start token** instead.\n\n```typescript\nimport { LiveActivities } from '@ciabosoftwaresolutions/capacitor-live-activities';\n\n// The token is type-level (not per-activity). Register it once at launch and\n// whenever it rotates. Send it to your server keyed by user/device.\nawait LiveActivities.addListener('pushToStartTokenUpdated', async (event) => {\n  await yourServer.registerPushToStartToken({ token: event.token });\n});\n\n// Optional: read the current value (may be null until the system issues it)\nconst { token } = await LiveActivities.getPushToStartToken();\n```\n\n**Server payload to start an activity** — `POST https://api.push.apple.com/3/device/<push-to-start-token>`:\n\n```\napns-topic       <your.bundle.id>.push-type.liveactivity\napns-push-type   liveactivity\n```\n```json\n{\n  \"aps\": {\n    \"timestamp\": 1700000000,\n    \"event\": \"start\",\n    \"attributes-type\": \"LiveActivityAttributes\",\n    \"attributes\": { \"activityType\": \"order-tracking\" },\n    \"content-state\": {\n      \"title\": \"Order received\",\n      \"subtitle\": \"Preparing your food\",\n      \"progress\": 0.1,\n      \"icon\": \"fork.knife\"\n    },\n    \"alert\": { \"title\": \"Order received\", \"body\": \"We're preparing your food\" }\n  }\n}\n```\n\nAfter the activity starts, the device fires `pushTokenUpdated` with that activity's **per-activity** token — store it to send subsequent `update` / `end` pushes.\n\n> Push-to-start is **iOS 17.2+** only. On older iOS and Android, `getPushToStartToken()` returns `{ token: null }` — fall back to starting the activity in-app.\n\n---\n\n### Sending from a Java backend\n\nBoth the per-activity and push-to-start flows are plain APNs HTTP/2 requests. The cleanest Java option is [**Pushy**](https://github.com/jchambers/pushy) (`com.eatthepath:pushy`), which has first-class Live Activity support.\n\n```java\n// One-time client, signed with your APNs Auth Key (.p8)\nApnsClient client = new ApnsClientBuilder()\n    .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)\n    .setSigningKey(ApnsSigningKey.loadFromPkcs8File(\n        new File(\"AuthKey_ABC123.p8\"), \"YOUR_TEAM_ID\", \"YOUR_KEY_ID\"))\n    .build();\n\n// Build the content-state — keys MUST match LiveActivityState fields\nString payload = new SimpleApnsPayloadBuilder()\n    .setContentState(Map.of(\n        \"title\", \"Order arriving now\",\n        \"subtitle\", \"1 stop away\",\n        \"progress\", 0.95,\n        \"progressColor\", \"#4ade80\"))\n    .setEvent(\"update\")                      // \"start\" | \"update\" | \"end\"\n    .setTimestamp(Instant.now().getEpochSecond())\n    .build();\n\n// topic = <bundleId>.push-type.liveactivity, push type = LIVE_ACTIVITY\nvar push = new SimpleApnsPushNotification(\n    token,                                   // per-activity OR push-to-start token\n    \"com.yourcompany.yourapp.push-type.liveactivity\",\n    payload,\n    null,\n    DeliveryPriority.IMMEDIATE,\n    PushType.LIVE_ACTIVITY);\n\nclient.sendNotification(push).get();\n```\n\nFor a **`start`** push, also include the `attributes-type` and `attributes` in the content-state builder (Pushy exposes `setEvent(\"start\")` plus raw map fields) and send to the **push-to-start** token.\n\n**Token bookkeeping your backend needs:**\n\n| Token | From | Used for |\n|---|---|---|\n| Push-to-start | `pushToStartTokenUpdated` | `event: \"start\"` — launch new activities |\n| Per-activity | `pushTokenUpdated` (after start) | `event: \"update\"` / `\"end\"` for that activity |\n\n---\n\n### Android — FCM (optional)\n\nFirebase is **not** a hard dependency. The plugin uses reflection to detect Firebase at runtime — if it isn't in the project `getPushToken()` returns `null` cleanly and everything else still works.\n\n#### Add Firebase to your project (opt-in)\n\n1. Follow the [Firebase Android setup guide](https://firebase.google.com/docs/android/setup) to add `google-services.json` to `android/app/`.\n\n2. In `android/build.gradle` (project level):\n   ```gradle\n   classpath 'com.google.gms:google-services:4.4.2'\n   ```\n\n3. In `android/app/build.gradle`:\n   ```gradle\n   apply plugin: 'com.google.gms.google-services'\n   implementation 'com.google.firebase:firebase-messaging:24.1.0'\n   ```\n\n4. Run `npx cap sync`.\n\n#### Get the FCM token\n\n```typescript\n// activityId is ignored on Android — FCM tokens are per-device, not per-activity\nconst { token, type } = await LiveActivities.getPushToken({ activityId });\n// type === 'fcm' when Firebase is present, null otherwise\nif (token) {\n  await yourServer.registerFcmToken({ token });\n}\n```\n\n#### Server payload\n\nSend a FCM **data message** (not a notification message) so the OS delivers it even when the app is in the background:\n\n```json\n{\n  \"message\": {\n    \"token\": \"<fcm-device-token>\",\n    \"data\": {\n      \"liveActivityId\": \"your-activity-id\",\n      \"title\": \"Order arriving now\",\n      \"subtitle\": \"1 stop away\",\n      \"progress\": \"0.95\"\n    }\n  }\n}\n```\n\nYour app should handle the incoming FCM data message (via a Firebase `onMessageReceived` service) and call `LiveActivities.update()` with the new state.\n\n> On Android the OS does not update the notification directly from the push payload — FCM wakes your app, your app calls `update()`. This is by design and means your update logic stays in one place (TypeScript).\n\n---\n\n## Customising the iOS Widget UI\n\nThe default widget renders **icon · title · subtitle · progress bar** using a dark frosted background. To customise it:\n\n1. Open `LiveActivityWidget.swift` in your Widget Extension target.\n2. Edit `LockScreenView` (Lock Screen / banner) and the `DynamicIsland` regions.\n3. Add extra fields to `LiveActivityAttributes.ContentState` — they flow through automatically in the `extras` dictionary.\n\n### Built-in colors & sizing — no Swift needed\n\nYou can restyle the default widget entirely from JavaScript by passing these `state` fields. All are optional and iOS-only (Android ignores them gracefully):\n\n```typescript\nawait LiveActivities.start({\n  attributes: { activityType: 'order' },\n  state: {\n    title: 'Pizza on the way',\n    subtitle: 'Arriving in',\n    icon: 'fork.knife',\n    timerEnd: Date.now() / 1000 + 150,\n\n    // 🎨 Colors — hex string, with or without leading \"#\"\n    backgroundColor: '#1a1a2e',   // Lock Screen + expanded island background\n    progressColor:   '#4ade80',   // progress bar / ring / timer bar\n    textColor:       '#ffffff',   // title, subtitle, countdown text\n    iconColor:       '#4ade80',   // SF Symbol tint\n    keylineTint:     '#4ade80',   // colored glow around the expanded island\n\n    // 📏 Progress bar sizing\n    progressBarHeight: 8,         // points, 2–20 (default ~4)\n    progressBarRadius: 4,         // points (default: pill / half the height)\n  },\n});\n```\n\n| Field | Affects | Default |\n|---|---|---|\n| `backgroundColor` | Lock Screen banner + expanded Dynamic Island background | dark translucent |\n| `progressColor` | Progress bar, ring, and timer bar fill | white |\n| `textColor` | Title, subtitle, countdown text | white |\n| `iconColor` | SF Symbol icon tint | white |\n| `keylineTint` | The colored outline that wraps the **expanded** Dynamic Island | none |\n| `progressBarHeight` | Linear progress bar thickness (points, 2–20) | system (~4) |\n| `progressBarRadius` | Linear progress bar corner radius | pill (half the height) |\n\n### About the Dynamic Island \"width\"\n\nThe **physical size of the Dynamic Island is controlled by iOS** — there is no API to force a \"full-length\" or wider compact island. Apple manages the compact and minimal presentations automatically based on what other activities are running. What you *can* control to make it feel branded and prominent:\n\n- **`keylineTint`** — adds a colored glow border around the **expanded** island (shown on long-press). This is the closest thing to a full-length colored island Apple exposes.\n- **`backgroundColor`** — fills the expanded island and Lock Screen banner with your brand color.\n- **The expanded layout** — the leading / trailing / center / bottom regions in `LiveActivityWidget.swift` are fully yours to lay out. Add images, multiple rows, buttons (iOS 17+), etc.\n\nThe **compact** presentation (the small pill around the camera) is deliberately constrained by iOS to a tiny leading + trailing slot — design for a single glanceable icon + value there.\n\n---\n\n## API\n\n<docgen-index>\n\n* [`isSupported()`](#issupported)\n* [`areActivitiesEnabled()`](#areactivitiesenabled)\n* [`start(...)`](#start)\n* [`update(...)`](#update)\n* [`end(...)`](#end)\n* [`getActiveActivities()`](#getactiveactivities)\n* [`getPushToken(...)`](#getpushtoken)\n* [`getPushToStartToken()`](#getpushtostarttoken)\n* [`addListener('activityStateChanged' | 'pushTokenUpdated' | 'pushToStartTokenUpdated', ...)`](#addlisteneractivitystatechanged--pushtokenupdated--pushtostarttokenupdated-)\n* [`removeAllListeners()`](#removealllisteners)\n* [Interfaces](#interfaces)\n\n</docgen-index>\n\n<docgen-api>\n<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->\n\n### isSupported()\n\n```typescript\nisSupported() => any\n```\n\nReturns true when the current platform and OS version support Live Activities\n(iOS 16.2+ with the feature enabled by the user) or Live Updates (Android 16+).\nOn older Android versions this still returns true because the plugin falls back\nto a sticky notification.\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### areActivitiesEnabled()\n\n```typescript\nareActivitiesEnabled() => any\n```\n\niOS only — returns true if the user has Live Activities enabled for this app\nin Settings. Always true on Android.\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### start(...)\n\n```typescript\nstart(options: StartOptions) => any\n```\n\nStart a new Live Activity / Live Update notification.\nResolves with an `activityId` you must store to call `update()` and `end()`.\n\n| Param         | Type                                                  |\n| ------------- | ----------------------------------------------------- |\n| **`options`** | <code><a href=\"#startoptions\">StartOptions</a></code> |\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### update(...)\n\n```typescript\nupdate(options: UpdateOptions) => any\n```\n\nPush a state update to a running Live Activity.\n\n| Param         | Type                                                    |\n| ------------- | ------------------------------------------------------- |\n| **`options`** | <code><a href=\"#updateoptions\">UpdateOptions</a></code> |\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### end(...)\n\n```typescript\nend(options: EndOptions) => any\n```\n\nEnd a Live Activity and optionally show a final state.\n\n| Param         | Type                                              |\n| ------------- | ------------------------------------------------- |\n| **`options`** | <code><a href=\"#endoptions\">EndOptions</a></code> |\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### getActiveActivities()\n\n```typescript\ngetActiveActivities() => any\n```\n\nReturns all currently active activity IDs started by this app.\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### getPushToken(...)\n\n```typescript\ngetPushToken(options: { activityId: string; }) => any\n```\n\nGet the push token for a running Live Activity (iOS) or the FCM device\ntoken (Android).\n\n- **iOS**: pass the `activityId` returned by `start()`. The token is\n  specific to that activity and should be sent to your server immediately.\n  It may rotate — listen to `pushTokenUpdated` for changes.\n- **Android**: `activityId` is ignored. Returns the FCM registration token\n  if Firebase is configured in the project, otherwise `null`.\n\nThe app-driven update path (`update()`) works without any push token.\nYou only need this for *server-side* push-driven updates.\n\n| Param         | Type                                 |\n| ------------- | ------------------------------------ |\n| **`options`** | <code>{ activityId: string; }</code> |\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### getPushToStartToken()\n\n```typescript\ngetPushToStartToken() => any\n```\n\niOS 17.2+ only — get the **push-to-start** token. Unlike `getPushToken`,\nthis token is **not tied to a specific activity** — it lets your server\nSTART a brand-new Live Activity remotely, even if the app has never called\n`start()`. Ideal for order tracking, appointment reminders, etc.\n\nSend this token to your server. The system may issue it slightly after\nlaunch, so prefer listening to `pushToStartTokenUpdated` and treat this\ngetter as a \"current value\" check.\n\nReturns `{ token: null }` on iOS &lt; 17.2 and on Android.\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### addListener('activityStateChanged' | 'pushTokenUpdated' | 'pushToStartTokenUpdated', ...)\n\n```typescript\naddListener(eventName: 'activityStateChanged' | 'pushTokenUpdated' | 'pushToStartTokenUpdated', listenerFunc: (event: ActivityStateChangedEvent | PushTokenUpdatedEvent | PushToStartTokenUpdatedEvent) => void) => any\n```\n\nSubscribe to Live Activity events.\n\n- **`activityStateChanged`** — iOS only. Fired when the system changes the\n  state of a Live Activity (e.g. the user dismisses it from the Lock Screen).\n  Payload: `{ activityId: string, activityState: 'active' | 'ended' | 'dismissed' }`\n\n- **`pushTokenUpdated`** — iOS only. Fired when the per-activity ActivityKit\n  push token is first issued or rotated. Re-send it to your server so it can\n  continue delivering APNs **updates** to that activity.\n  Payload: `{ activityId: string, token: string, type: 'apns' }`\n\n- **`pushToStartTokenUpdated`** — iOS 17.2+ only. Fired when the type-level\n  push-to-start token is issued or rotated. Re-send it to your server so it\n  can **start** new activities remotely.\n  Payload: `{ token: string, type: 'apns' }`\n\n| Param              | Type                                                                                                                                                                                                                                          |\n| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`eventName`**    | <code>'activityStateChanged' \\| 'pushTokenUpdated' \\| 'pushToStartTokenUpdated'</code>                                                                                                                                                        |\n| **`listenerFunc`** | <code>(event: <a href=\"#activitystatechangedevent\">ActivityStateChangedEvent</a> \\| <a href=\"#pushtokenupdatedevent\">PushTokenUpdatedEvent</a> \\| <a href=\"#pushtostarttokenupdatedevent\">PushToStartTokenUpdatedEvent</a>) =&gt; void</code> |\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### removeAllListeners()\n\n```typescript\nremoveAllListeners() => any\n```\n\n**Returns:** <code>any</code>\n\n--------------------\n\n\n### Interfaces\n\n\n#### StartOptions\n\n| Prop                    | Type                                                                      | Description                                                                                                                 |\n| ----------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| **`attributes`**        | <code><a href=\"#liveactivityattributes\">LiveActivityAttributes</a></code> |                                                                                                                             |\n| **`state`**             | <code><a href=\"#liveactivitystate\">LiveActivityState</a></code>           |                                                                                                                             |\n| **`staleAfterSeconds`** | <code>number</code>                                                       | iOS only — how long (seconds) the activity stays visible after `end()`. Defaults to 0 (dismissed immediately). Max 4 hours. |\n\n\n#### LiveActivityAttributes\n\nStatic data for a Live Activity — set once at creation, never changes.\nKeep this small; use `state` for anything that updates.\n\n| Prop               | Type                | Description                                                                 |\n| ------------------ | ------------------- | --------------------------------------------------------------------------- |\n| **`activityType`** | <code>string</code> | Unique identifier you assign (e.g. \"order-123\"). Used to correlate updates. |\n\n\n#### LiveActivityState\n\nDynamic data for a Live Activity — can be pushed via `update()` at any time.\n\n| Prop                    | Type                 | Description                                                                                                                                                                                                                                                                                                                                            |\n| ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **`title`**             | <code>string</code>  | Primary headline shown on the Lock Screen / Dynamic Island.                                                                                                                                                                                                                                                                                            |\n| **`subtitle`**          | <code>string</code>  | Secondary line of text.                                                                                                                                                                                                                                                                                                                                |\n| **`progress`**          | <code>number</code>  | Optional progress value between 0.0 and 1.0.                                                                                                                                                                                                                                                                                                           |\n| **`icon`**              | <code>string</code>  | Optional SF Symbol name (iOS) or Android drawable name for a status icon.                                                                                                                                                                                                                                                                              |\n| **`backgroundColor`**   | <code>string</code>  | iOS only — hex color string for the activity background tint. Applied to the Lock Screen banner and expanded Dynamic Island background. Accepts 6-digit hex with or without `#` prefix, e.g. `\"#1a1a2e\"` or `\"1a1a2e\"`. Defaults to a dark semi-transparent fill when omitted.                                                                         |\n| **`progressColor`**     | <code>string</code>  | iOS only — hex color string for the progress bar, progress ring and timer bar. Defaults to white when omitted.                                                                                                                                                                                                                                         |\n| **`textColor`**         | <code>string</code>  | iOS only — hex color string for title and subtitle text. Defaults to white when omitted.                                                                                                                                                                                                                                                               |\n| **`iconColor`**         | <code>string</code>  | iOS only — hex color string for the SF Symbol icon. Defaults to white when omitted.                                                                                                                                                                                                                                                                    |\n| **`keylineTint`**       | <code>string</code>  | iOS only — hex color string for the keyline (the thin colored outline that wraps the **expanded** Dynamic Island). A common way to give the island a branded, \"full-length\" colored glow. Defaults to no keyline when omitted.                                                                                                                         |\n| **`progressBarHeight`** | <code>number</code>  | iOS only — thickness in points of the linear progress bar shown on the Lock Screen and in the expanded Dynamic Island bottom region. Range 2–20. Defaults to the system thickness (~4) when omitted.                                                                                                                                                   |\n| **`progressBarRadius`** | <code>number</code>  | iOS only — corner radius in points applied to the linear progress bar when `progressBarHeight` is set. Defaults to half the bar height (pill shape).                                                                                                                                                                                                   |\n| **`timerEnd`**          | <code>number</code>  | iOS only — Unix timestamp (seconds since epoch) when the timer ends. When set, the widget renders a live countdown and an auto-animating progress bar. The system updates the display every second automatically — no `update()` calls needed from JavaScript. Example — start a 2m 30s countdown: ```typescript timerEnd: Date.now() / 1000 + 150 ``` |\n| **`timerStart`**        | <code>number</code>  | iOS only — Unix timestamp when the timer started. Used alongside `timerEnd` to compute the progress ring fill. Defaults to `Date.now()` at the moment `start()` is called if omitted.                                                                                                                                                                  |\n| **`timerCountsDown`**   | <code>boolean</code> | iOS only — direction of the timer progress bar/ring. - `true` (default) — bar starts **full** and drains right-to-left as time runs out. - `false` — bar starts **empty** and fills left-to-right as time elapses. The countdown **text** always shows remaining time regardless of this setting.                                                      |\n\n\n#### UpdateOptions\n\n| Prop             | Type                                                            | Description                                                                                                |\n| ---------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| **`activityId`** | <code>string</code>                                             |                                                                                                            |\n| **`state`**      | <code><a href=\"#liveactivitystate\">LiveActivityState</a></code> |                                                                                                            |\n| **`alertTitle`** | <code>string</code>                                             | iOS only — alert the user with a banner when this update arrives. Ignored if the app is in the foreground. |\n| **`alertBody`**  | <code>string</code>                                             |                                                                                                            |\n\n\n#### EndOptions\n\n| Prop                  | Type                                                            | Description                                                                                                                                                                           |\n| --------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`activityId`**      | <code>string</code>                                             |                                                                                                                                                                                       |\n| **`finalState`**      | <code><a href=\"#liveactivitystate\">LiveActivityState</a></code> | Final state to display before the activity is dismissed. If omitted the last known state is used.                                                                                     |\n| **`dismissalPolicy`** | <code>'immediate' \\| 'default' \\| 'after-delay'</code>          | iOS only — dismiss the activity immediately or leave it on screen for a short period so the user sees the final state. 'immediate' \\| 'default' \\| 'after-delay' (default: 'default') |\n\n\n#### ActivityInfo\n\n| Prop               | Type                                            | Description                                                                    |\n| ------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------ |\n| **`activityId`**   | <code>string</code>                             |                                                                                |\n| **`activityType`** | <code>string</code>                             |                                                                                |\n| **`state`**        | <code>'active' \\| 'ended' \\| 'dismissed'</code> | 'active' \\| 'ended' \\| 'dismissed' — iOS only; Android always returns 'active' |\n\n\n#### PushTokenResult\n\n| Prop        | Type                                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ----------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`token`** | <code>string \\| null</code>          | The push token for this specific Live Activity (iOS) or the FCM registration token for the device (Android). iOS — This is an ActivityKit push token unique to this activity instance. Send it to your server and use it to deliver APNs `liveActivity` payloads directly (no Firebase needed). Changes over the activity's lifetime; listen to `pushTokenUpdated` to receive the latest value. Android — This is the standard FCM registration token for the device. Returns `null` when Firebase is not configured in the project. Send it to your server and deliver updates via an FCM data message (see README → Push-driven updates). |\n| **`type`**  | <code>'apns' \\| 'fcm' \\| null</code> | 'apns' on iOS, 'fcm' on Android, null when unavailable.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n\n\n#### ActivityStateChangedEvent\n\n| Prop                | Type                                            | Description               |\n| ------------------- | ----------------------------------------------- | ------------------------- |\n| **`activityId`**    | <code>string</code>                             |                           |\n| **`activityState`** | <code>'active' \\| 'ended' \\| 'dismissed'</code> | New state of the activity |\n\n\n#### PushTokenUpdatedEvent\n\n| Prop             | Type                         |\n| ---------------- | ---------------------------- |\n| **`activityId`** | <code>string</code>          |\n| **`token`**      | <code>string</code>          |\n| **`type`**       | <code>'apns' \\| 'fcm'</code> |\n\n\n#### PushToStartTokenUpdatedEvent\n\n| Prop        | Type                | Description                                                                  |\n| ----------- | ------------------- | ---------------------------------------------------------------------------- |\n| **`token`** | <code>string</code> | The push-to-start token — send to your server to launch activities remotely. |\n| **`type`**  | <code>'apns'</code> |                                                                              |\n\n\n#### PluginListenerHandle\n\n| Prop         | Type                      |\n| ------------ | ------------------------- |\n| **`remove`** | <code>() =&gt; any</code> |\n\n</docgen-api>\n\n---\n\n## FAQ — iOS Xcode setup troubleshooting\n\n### \"Missing package product 'CapApp-SPM'\"\n\nThis happens when Xcode loses its reference to the local `CapApp-SPM` package — most commonly after deleting and recreating the `ios/` folder.\n\n**Fix:**\n1. In Xcode: **File → Add Package Dependencies → Add Local...**\n2. Navigate to `ios/App/CapApp-SPM` and click **Add Package**\n3. When prompted for a target, select **none** — `CapApp-SPM` is managed by Capacitor internally\n4. Xcode will resolve the package and the error disappears\n\n---\n\n### \"Unable to find module dependency: 'Capacitor'\" in AppDelegate\n\nAfter fixing the `CapApp-SPM` reference above, the `Capacitor` module still needs to be linked to the app target.\n\n**Fix:**\n- Select your **main app target** → **General** → **Frameworks, Libraries, and Embedded Content** → **+** → search for **CapApp-SPM** → **Add**\n\n---\n\n### `npx cap sync` keeps re-adding the plugin to `CapApp-SPM/Package.swift`\n\nWhen you include the plugin's Swift files directly in the Xcode project (recommended for local development), `npx cap sync` will re-add the plugin to `CapApp-SPM/Package.swift` on every run, causing duplicate symbol errors on the next build.\n\nRun this after every `npx cap sync` to clean it out:\n\n```bash\ncd example && node -e \"\nconst fs = require('fs');\nconst f = 'ios/App/CapApp-SPM/Package.swift';\nlet c = fs.readFileSync(f, 'utf8');\nc = c.replace(/,\\s*\\.package\\(name:.*?capacitor-live-activities.*?\\)/s, '');\nc = c.replace(/,\\s*\\.product\\(name: \\\"CiabosoftwaresolutionsCapacitorLiveActivities\\\".*?\\)/s, '');\nfs.writeFileSync(f, c);\nconsole.log('Cleaned');\n\"\n```\n\nOr add it as an npm script in your project's `package.json` so you can run `npm run sync:ios` instead:\n\n```json\n\"scripts\": {\n  \"sync:ios\": \"npx cap sync ios && node -e \\\"const fs=require('fs'),f='ios/App/CapApp-SPM/Package.swift';let c=fs.readFileSync(f,'utf8');c=c.replace(/,\\\\s*\\\\.package\\\\(name:.*?capacitor-live-activities.*?\\\\)/s,'');c=c.replace(/,\\\\s*\\\\.product\\\\(name:\\\\s*\\\\\\\"CiabosoftwaresolutionsCapacitorLiveActivities\\\\\\\".*?\\\\)/s,'');fs.writeFileSync(f,c);console.log('CapApp-SPM cleaned');\\\"\"\n}\n```\n\n---\n\n### Plugin not found / \"not implemented on ios\"\n\nThe plugin's Swift files need to be compiled into the app directly. Include them from `node_modules/@ciabosoftwaresolutions/capacitor-live-activities/ios/Plugin/`:\n\n1. Drag `LiveActivitiesPlugin.swift` and `LiveActivityManager.swift` into Xcode\n2. ✅ **Copy items if needed**\n3. **Target Membership:**\n   - `LiveActivityManager.swift` → ✅ main app + ✅ `LiveActivityWidget`\n   - `LiveActivitiesPlugin.swift` → ✅ main app only\n\n---\n\n### \"Invalid redeclaration of 'LiveActivityWidgetBundle'\"\n\nXcode generates two files when adding a Widget Extension — delete the generated `LiveActivityWidgetBundle.swift`:\n\nRight-click → **Delete → Move to Trash**\n\n---\n\n### \"Plugin with id 'org.jetbrains.kotlin.android' not found\" (Android)\n\nAdd the Kotlin classpath to the plugin's `android/build.gradle`:\n\n```groovy\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25'\n    }\n}\n```\n\n---\n\n### Notifications not appearing on Android\n\nCall `requestPermissions()` before `start()` on Android 13+:\n\n```typescript\nconst { notifications } = await LiveActivities.requestPermissions();\nif (notifications === 'granted') {\n  await LiveActivities.start({ ... });\n}\n```\n\n---\n\n## Contributing\n\nPRs welcome! Please open an issue first for non-trivial changes.\n\n```bash\n# Install dependencies and build the plugin\nnpm install\nnpm run build\n\n# Run the example app\ncd example && npm install && npm run build\n```\n\n---\n\n## Support\n\nIf this plugin helped you ship faster, consider buying us a coffee — it helps keep the project maintained and free for everyone. ☕\n\n[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-yellow?logo=buy-me-a-coffee&logoColor=white)](https://buymeacoffee.com/ciabosoftwaresolutions)\n\n---\n\n## License\n\nMIT © [Ciabo Software Solutions](https://github.com/ciabosoftwaresolutions)\n","readmeFilename":"README.md"}