# Capacitor Integrate the Airship SDK into your Capacitor applications for iOS and Android. # Deep Links > Configure deep link handling for Airship messaging. Deep linking allows Airship messaging to open your app to specific resources or screens. When a user interacts with a message (notification, in-app message, etc.), the deep link can navigate them directly to the relevant content in your app. ## Listening for deep links The SDK provides a way to listen for deep links so you can handle them in your app. This handler receives all deep links except for Message Center and Preference Center display requests, which are handled automatically by their respective features. > **Note:** For Message Center and Preference Center display requests, see [Message Center: Getting Started](https://www.airship.com/docs/developer/sdk-integration/capacitor/message-center/getting-started/) and [Preference Center: Getting Started](https://www.airship.com/docs/developer/sdk-integration/capacitor/preference-center/getting-started/). ```js Airship.onDeepLink(event => { var deepLink = event.deepLink; // Handle deep link }); ``` # Actions > Airship Actions provide a convenient way to automatically perform tasks by name in response to push notifications, Message Center App Page interactions, and JavaScript. An action describes a function, which takes an optional argument and performs a predefined task, producing an optional result. Actions may restrict or vary the work they perform depending on the arguments they receive, which may include type introspection and runtime context. The Airship SDK includes built-in actions for common tasks, and you can create custom actions to extend functionality. For a complete list of available built-in actions, see the [Actions User Guide](https://www.airship.com/docs/guides/messaging/messages/actions/). ## Running Actions You can run actions programmatically using the `Airship.actions.run()` method. The method returns a Promise that resolves with the action result. The action value can be a string, number, boolean, null, object, or array. **Running an action** ```typescript // Run an action with a string value using async/await try { const result = await Airship.actions.run("action_name", "action_value"); console.log("Action result:", result); } catch (error) { console.error("Action error:", error); } // Run an action with an object value try { const result = await Airship.actions.run("action_name", { key: "value", number: 42 }); console.log("Action result:", result); } catch (error) { console.error("Action error:", error); } // Run an action without a value try { const result = await Airship.actions.run("action_name"); console.log("Action result:", result); } catch (error) { console.error("Action error:", error); } // Run an action using Promise.then() Airship.actions.run("action_name", "action_value") .then((result) => { console.log("Action result:", result); }) .catch((error) => { console.error("Action error:", error); }); ``` ## Custom Actions You can register custom actions with an [Airship extender](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/) and with native SDK APIs. See the native platform documentation for details: - [iOS Actions](https://www.airship.com/docs/developer/sdk-integration/apple/actions/) - [Android Actions](https://www.airship.com/docs/developer/sdk-integration/android/actions/) # Feature Flags > {{< glossary_definition "feature_flag" >}} ## Accessing flags The Airship SDK will refresh feature flags when the app is brought to the foreground. If a feature flag is accessed before the foreground refresh completes, or after the foreground refresh has failed, feature flags will be refreshed during flag access. Feature flags will only be updated once per session and will persist for the duration of each session. Once [defined in the dashboard](https://www.airship.com/docs/guides/experimentation/feature-flags/#create-feature-flags), a feature flag can be accessed by its name in the SDK after `takeOff`. ```js const flag = await Airship.featureFlagManager.flag("YOUR_FLAG_NAME") if (flag.isEligible) { // Do something with the flag } else { // Disable feature or use default behavior } ``` ## Tracking interaction To generate the [Feature Flag Interaction Event](https://www.airship.com/docs/developer/rest-api/connect/schemas/events/#feature-flag-interaction), you must manually call `trackInteraction` with the feature flag. Analytics must be enabled. See: [Data Collection: Privacy Manager](https://www.airship.com/docs/developer/sdk-integration/capacitor/data-collection/privacy-manager/). ```js await Airship.featureFlagManager.trackInteraction(flag) ``` ## Error handling If a feature flag allows evaluation with stale data, the SDK evaluates the flag if a definition for the flag is found. Otherwise, feature flag evaluation depends on updated local state. If the SDK cannot evaluate a flag because data cannot be fetched, the SDK returns or raises an error. The app can either treat the error as the flag being ineligible or retry at a later time. ```js try { const flag = await Airship.featureFlagManager.flag("another_rad_flag") } catch (error) { console.log("error: " + error) } ``` # Live Activities > Integrate Live Activities into your Capacitor app to display real-time updates on the iOS Lock Screen and Dynamic Island. {{< badge "axp" >}} For the push API method, see the [iOS Live Activities](https://www.airship.com/docs/guides/messaging/features/ios-live-activities/) messaging guide. See also the [iOS Live Activities](https://www.airship.com/docs/guides/features/messaging/live-activities-updates/) feature guide. ### App setup Using the [AirshipPluginExtender](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/), make a call to `LiveActivityManager.shared.setup` to configure any Live Activities for the app. Call `configurator.register` for each Live Activity type that your application defines and include a block on how to parse the name of the activity that you will use to track on Airship. This name will be used to send updates through APNS. ```swift import Foundation import AirshipKit import AirshipFrameworkProxy import ActivityKit // This class header is required to be automatically picked up by the Airship plugin: @objc(AirshipPluginExtender) public class AirshipPluginExtender: NSObject, AirshipPluginExtenderProtocol { public static func onAirshipReady() { if #available(iOS 16.1, *) { // Will throw if called more than once try? LiveActivityManager.shared.setup { configurator in // Call for each Live Activity type await configurator.register(forType: Activity.self) { attributes in // Track this property as the Airship name for updates attributes.gameID } } } // other setup } } ``` ### Starting Live Activities For any Live Activities configured, you can start a new one using the `start` method: ```typescript Airship.iOS.liveActivityManager.start({ attributesType: 'SportsActivityAttributes', content: { state: { status: 'Game Pending', }, relevanceScore: 0.0, }, attributes: { gameID: 'sports-game-123', }, }); ``` ### Updating Live Activities To update, use `update`, but you will need the activity ID. ```typescript const activities = await Airship.iOS.liveActivityManager.listAll(); const activity = activities.find( (activity) => activity.attributes.gameID === 'sports-game-123' ); if (activity) { Airship.iOS.liveActivityManager.update({ activityId: activity.id, content: { state: { status: "Game starting!" } relevanceScore: 0.0, }, }); } ``` ### Ending Live Activities To end is similar to `update`. Use `end` with the activity ID: ```typescript const activities = await Airship.iOS.liveActivityManager.listAll(); const activity = activities.find( (activity) => activity.attributes.gameID === 'sports-game-123' ); if (activity) { Airship.iOS.liveActivityManager.end({ activityId: activity.id, dismissalPolicy: { type: "default" } }); } ``` # Live Updates > Integrate Live Updates into your Capacitor app to update content in real-time without requiring an app update. {{< badge "axp" >}} For the push API method, see the [Android Live Updates](https://www.airship.com/docs/guides/messaging/features/android-live-updates/) messaging guide. See also the [Android Live Updates](https://www.airship.com/docs/guides/features/messaging/live-activities-updates/) feature guide. ### Creating a handler The Airship SDK supports two types of Live Update handlers: * `NotificationLiveUpdateHandler` — Displays a notification with a custom layout, with content updated by the Live Update. * `CustomLiveUpdateHandler` — Receives Live Update events and provides flexibility to display content using a custom implementation. This can be used to power home screen widgets, views embedded in the app, and more. Each handler type has two different interfaces that may be implemented, to support suspending or callback-based code: * `SuspendLiveUpdateNotificationHandler` * `CallbackLiveUpdateNotificationHandler` * `SuspendLiveUpdateCustomHandler` * `CallbackLiveUpdateCustomHandler` The following `SampleLiveUpdateHandler` reads content from the Live Update payload and displays scores for a sports game in a custom notification layout, using `RemoteViews`: ```kotlin class SampleLiveUpdateHandler : SuspendLiveUpdateNotificationHandler() { override suspend fun onUpdate( context: Context, event: LiveUpdateEvent, update: LiveUpdate ): LiveUpdateResult { // Read content_state fields from the Live Update payload val teamOneScore = update.content.opt("team_one_score").getInt(0).toString() val teamTwoScore = update.content.opt("team_two_score").getInt(0).toString() val statusUpdate = update.content.opt("status_update").optString() // Expanded notification layout val bigLayout = RemoteViews(context.packageName, R.layout.sports_big).apply { setTextViewText(R.id.teamOneScore, teamOneScore) setTextViewText(R.id.teamTwoScore, teamTwoScore) setTextViewText(R.id.statusUpdate, statusUpdate) } // Collapsed notification layout val smallLayout = RemoteViews(context.packageName, R.layout.sports_small).apply { setTextViewText(R.id.teamOneScore, teamOneScore) setTextViewText(R.id.teamTwoScore, teamTwoScore) } // Create the notification builder val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_EVENT) .setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setCustomContentView(smallLayout) .setCustomBigContentView(bigLayout) // Return 'ok' with the notification builder. // The Airship SDK will handle posting the notification. // Returning LiveUpdateResult.cancel() will end the Live Update and dismiss the notification. return LiveUpdateResult.ok(builder) } companion object { private const val NOTIFICATION_CHANNEL_ID = "sports" } } ``` ### Registering a handler Handlers must be registered with `LiveUpdateManager` in order to receive Live Update events. This should be done *once* after `takeOff`. Using the [AirshipPluginExtender](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/), register the types. ```kotlin @Keep public final class AirshipExtender: AirshipPluginExtender { override fun onAirshipReady(context: Context, airship: UAirship) { LiveUpdateManager.shared().run { register(type = "notification", handler = SampleLiveUpdateHandler()) } } } ``` > **Note:** The `type` used above, `"notification"`, is used to map Live Update events to the corresponding handler in your app. > The value can be any string that is unique across all handlers registered by an app. This also allows a single handler to manage multiple Live Updates that each have a unique `name`. ### Starting Live Updates Live Updates can be started from within the app. ```typescript Airship.android.liveUpdateManager.start({ name: 'sports-game-123' type: 'notification', content: { team_one_score: 0, team_two_score: 0, status_update: 'Game started!' } }); ``` ### Updating Live Updates Live Updates can be updated from within the app. ```typescript Airship.android.liveUpdateManager.update({ name: 'sports-game-123' content: { team_one_score: 3, team_two_score: 0, status_update: 'Game started!' } }); ``` ### Ending Live Updates You can end a Live Update from within the app. ```typescript Airship.android.liveUpdateManager.end({ name: 'sports-game-123' content: { team_one_score: 9, team_two_score: 6, status_update: 'Game over!' } }); ``` ### Clearing all active Live Updates During development, it can be useful to reset Live Update tracking on app launch. This allows any Live Updates to be started fresh, even if they were already started during a previous launch. To end all currently active Live Updates, call the `clearAll()` method on `LiveUpdateManager`. ```typescript Airship.android.liveUpdateManager.clearAll(); ``` # Capacitor Plugin Changelog > The latest updates to the Airship Capacitor plugin. See the [SDK Support Policy](https://www.airship.com/docs/reference/sdk-support-policy/) for version coverage and maintenance windows. ## 6.0.0 May 8, 2026 Major release that updates Capacitor to 8.0. ### Changes - Updated Capacitor to 8.0 - Android `minSdkVersion` increased from 23 to 24 - Android Gradle wrapper updated to 8.14.3 ## 5.5.1 May 6, 2026 Patch release that fixes `Package.swift` not being included in the published npm package. ### Changes - Fixed `Package.swift` missing from the published npm package ## 5.5.0 May 1, 2026 Minor release that updates the Android SDK to 20.7.0 and the iOS SDK to 20.7.0. ### Changes - Updated Android SDK to [20.7.0](https://github.com/urbanairship/android-library/releases/tag/20.7.0) - Updated iOS SDK to [20.7.0](https://github.com/urbanairship/ios-library/releases/tag/20.7.0) ## 5.4.0 April 1, 2026 Minor release that updates the Android SDK to 20.6.1 and the iOS SDK to 20.6.0 ### Changes - Updated Android SDK to [20.6.1](https://github.com/urbanairship/android-library/releases/tag/20.6.1) - Updated iOS SDK to [20.6.0](https://github.com/urbanairship/ios-library/releases/tag/20.6.0) ## 5.3.0 March 18, 2026 Minor release that updates the Android SDK to 20.5.0 and the iOS SDK to 20.5.0. ### Changes - Updated Android SDK to [20.5.0](https://github.com/urbanairship/android-library/releases/tag/20.5.0) - Updated iOS SDK to [20.5.0](https://github.com/urbanairship/ios-library/releases/tag/20.5.0) ## 5.2.0 March 4, 2026 Minor release that updates the Android SDK to 20.4.0 and the iOS SDK to 20.4.0. ### Changes - Updated Android SDK to [20.4.0](https://github.com/urbanairship/android-library/releases/tag/20.4.0) - Updated iOS SDK to [20.4.0](https://github.com/urbanairship/ios-library/releases/tag/20.4.0) - Fixed missing back button on iOS when displaying a message with Airship.messageCenter.showMessageView ## 5.1.0 February 20, 2026 Minor release that fixes Airship failing to take off on iOS due to a plugin loader compatibility issue and updates the iOS SDK to 20.3.1 and the Android SDK to 20.2.2. ### Changes - Updated iOS SDK to [20.3.1](https://github.com/urbanairship/ios-library/releases/tag/20.3.1) - Updated Android SDK to [20.2.2](https://github.com/urbanairship/android-library/releases/tag/20.2.2) - Fixed iOS plugin loader to use the updated `onLoad()` protocol method - iOS minimum deployment target increased from 15.0 to 16.0 - Android compileSdkVersion updated to 36 - Android Kotlin version updated to 2.2.20 - Android Gradle Plugin updated to 8.13.0 ## 5.0.0 January 23, 2026 Major release that updates the Android SDK to 20.0.6 and the iOS SDK to 20.0.3 ### Changes - Updated Android SDK to [20.0.6](https://github.com/urbanairship/android-library/releases/tag/20.0.6) - Updated iOS SDK to [20.0.3](https://github.com/urbanairship/ios-library/releases/tag/20.0.3) ## 4.6.1 November 15, 2025 Patch release that fixes YouTube video playback in In-App Automation and Scenes. Applications that use YouTube videos in Scenes and non-html In-App Automations (IAA) must update to resolve playback errors. ### Changes - Updated Android SDK to [19.13.6](https://github.com/urbanairship/android-library/releases/tag/19.13.6) - Updated iOS SDK to [19.11.2](https://github.com/urbanairship/ios-library/releases/tag/19.11.2) ## 4.6.0 August 27, 2025 Patch release that updates the Android SDK to 19.11.0 and the iOS SDK to 19.8.3 ### Changes - Updated Android SDK to [19.11.0](https://github.com/urbanairship/android-library/releases/tag/19.11.0) - Updated iOS SDK to [19.8.3](https://github.com/urbanairship/ios-library/releases/tag/19.8.3) ## 4.5.0 July 31, 2025 Minor release that updates the Android SDK to 19.10.0 and the iOS SDK to 19.7.0 ### Changes - Updated Android SDK to [19.10.0](https://github.com/urbanairship/android-library/releases/tag/19.10.0) - Updated iOS SDK to [19.7.0](https://github.com/urbanairship/ios-library/releases/tag/19.7.0) [All Releases](https://github.com/urbanairship/capacitor-airship/releases) ## 4.4.0 June 27, 2025 Minor release that adds support for Android log privacy level configuration and updates the Android SDK to 19.9.1 and the iOS SDK to 19.6.1. ### Changes - Updated Android SDK to [19.9.1](https://github.com/urbanairship/android-library/releases/tag/19.9.1) - Updated iOS SDK to [19.6.1](https://github.com/urbanairship/ios-library/releases/tag/19.6.1) - Added Android `logPrivacyLevel` configuration support ## 4.3.1 May 9, 2025 Patch release that updates iOS SDK to 19.3.2 ### Changes - Updated iOS SDK to [19.3.2](https://github.com/urbanairship/ios-library/releases/tag/19.3.2) ## 4.3.0 May 2, 2025 Minor release that updates the Android SDK to 19.6.2 and the iOS SDK to 19.3.1. ### Changes - Updated Android SDK to [19.6.2](https://github.com/urbanairship/android-library/releases/tag/19.6.2) - Updated iOS SDK to [19.3.1](https://github.com/urbanairship/ios-library/releases/tag/19.3.1) - Added support for JSON attributes - Added new method `Airship.channel.waitForChannelId()` that waits for the channel ID to be created ## 4.2.0 March 27, 2025 Minor release that updates the Android SDK to 19.4.0 and the iOS SDK to 19.1.1 ### Changes - Updated Android SDK to [19.4.0](https://github.com/urbanairship/android-library/releases/tag/19.4.0) - Updated iOS SDK to [19.1.1](https://github.com/urbanairship/ios-library/releases/tag/19.1.1) ## 4.1.0 March 12, 2025 Major release that updates to the latest Airship SDKs and exposes the analytics session ID. ### Changes - Updated Android SDK to [19.3.0](https://github.com/urbanairship/android-library/releases/tag/19.3.0). - Updated iOS SDK to [19.1.0](https://github.com/urbanairship/ios-library/releases/tag/19.1.0). - Added `Airship.analytics.getSessionId()` method. ## 4.0.1 February 12, 2025 Patch release to fix the `MessageCenterUpdatedEvent` property `messageUnreadCount` on iOS. ### Changes - Fixed MessageCenterUpdatedEvent.messageUnreadCount on iOS. ## 4.0.0 February 11, 2025 Major release that updates the Android Airship SDK to 19.1.0 and iOS Airship SDK to 19.0.3 ### Changes - Updated Android SDK to [19.1.0](https://github.com/urbanairship/android-library/releases/tag/19.1.0) - Updated iOS SDK to [19.0.3](https://github.com/urbanairship/ios-library/releases/tag/19.0.3) - Updated Capacitor to 7.0.0 - Fixed SPM integration - iOS requires Xcode 16.2 and iOS 15+ - Android requires compileSdkVersion 35+ and minSdkVersion 23+ ## 3.1.0 December 7, 2024 Minor release that updates the Android Airship SDK to 18.5.0 and iOS Airship SDK to 18.13.0 ### Changes - Updated Android SDK to [18.5.0](https://github.com/urbanairship/android-library/releases/tag/18.5.0). - Updated iOS SDK to [18.13.0](https://github.com/urbanairship/ios-library/releases/tag/18.13.0). ## 3.0.1 November 27, 2024 Patch release that updates the iOS Airship SDK to 18.12.2 and Android Airship SDK to 18.4.2 ### Changes - Updated Android SDK to [18.4.2](https://github.com/urbanairship/android-library/releases/tag/18.4.2). - Updated iOS SDK to [18.12.2](https://github.com/urbanairship/ios-library/releases/tag/18.12.2). - Updated Android plugin to use the project compileSdk, minSdkVersion, and targetSdkVersion. The plugin still requires compileSdk to be set to 35+. ## 3.0.0 October 26, 2024 Major version that makes it easier to include Airship in a hybrid app. The only breaking change is when extending the AirshipPluginExtender protocol on java there is a new extendConfig(Contex, AirshipConfigOptions.Builder) method to implement. Most application will not be affected. ### Changes - Added new methods to the plugin extender to make hybrid app integrations easier - Updated Airship Android SDK to [18.3.3](https://github.com/urbanairship/android-library/releases/tag/18.3.3) - Fixed tracking live activities started from a push notification ## 2.4.0 October 16, 2024 Minor release that updates the native SDKs and fixes an issue with `Airship.messageCenter.getUnreadCount()` ### Changes - Updated Airship iOS SDK to [18.11.1](https://github.com/urbanairship/ios-library/releases/tag/18.11.1) - Fixed method binding for `Airship.messageCenter.getUnreadCount()` - Fixed MessageCenterPredicate not being applied to the OOTB Message Center UI on iOS ## 2.3.0 October 7, 2024 Minor release that updates to latest SDK versions and adds support for iOS Live Activities & Android Live Updates. ### Changes - Updated Airship Android SDK to [18.3.2](https://github.com/urbanairship/android-library/releases/tag/18.3.2) - Updated Airship iOS SDK to [18.10.0](https://github.com/urbanairship/ios-library/releases/tag/18.10.0) - Added new APIs to manage [iOS Live Activities](https://docs.airship.com/platform/mobile/ios-live-activities/) - Added new APIs to manage [Android Live Updates](https://docs.airship.com/platform/mobile/android-live-updates/) - Added a new [Plugin Extenders](http://localhost:1313/platform/mobile/setup/sdk/capacitor/#extending-airship) to modify the native Airship SDK after takeOff ## 2.2.0 September 25, 2024 Minor release that updates the iOS SDK to 18.9.2 and adds the method `Airship.messageCenter.showMessageCenter(messageId?: string)` that can be used to show the OOTB Message Center UI even if auto launching Message Center is disabled. This new functionality is useful if the application needs to route the user in the app before processing the display event while still being able to use the OOTB UI. ### Changes - Updated Airship iOS SDK to 18.9.2. - Added new `showMessageCenter(messageId?: string)` to `MessageCenter`. ## 2.1.0 September 16, 2024 Minor release that adds `notificationPermissionStatus` to the `PushNotificationStatus` object and a way to specify the fallback when requesting permissions and the permission is already denied. ### Changes - Updated Airship Android SDK to 18.3.0 - Updated Airship iOS SDK to 18.9.1 - Added `notificationPermissionStatus` to `PushNotificationStatus` - Added options to `enableUserNotifications` to specify the `PromptPermissionFallback` when enabling user notifications ## 2.0.1 July 16, 2024 Patch release that fixes a critical issue with iOS that prevents the Airship library from automatically initializing. Apps using 2.0.0 should update. ### Changes - Added missing files to the npm package for iOS ## 2.0.0 July 3, 2024 Major release to support Capacitor 6. ### Changes - Updated Airship Android SDK to 18.1.1 - Updated Airship iOS SDK to 18.5.0 - Added iOS logPrivacyLevel that can be set in the environments when calling takeOff ## 1.2.4 June 21, 2024 Patch release to fix a regression on iOS with In-App Automations, Scenes, and Surveys ignoring screen, version, and custom event triggers. Apps using those triggers that are on 1.2.3 should update. ### Changes - Updated iOS SDK to 18.4.1 - Fixed regression with triggers ## 1.2.3 June 20, 2024 Patch release that updates to latest iOS SDK. ### Changes - Updated iOS SDK to 18.4.0 ## 1.2.2 May 17, 2024 Patch release that updates to latest iOS SDK. ### Changes - Updated iOS SDK to 18.2.2 [View Older Releases](https://github.com/urbanairship/capacitor-airship/releases?q=created%3A%3C2024-05-15&expanded=true) # Capacitor Plugin Resources > API documentation, source code, and changelogs for the Airship Capacitor plugin. ## Platform Support {#platform-support} | Feature | iOS | Android | |----------------------------------------|-----|---------| | Push Notifications | ✅ | ✅ | | Live Activities | ✅ | ❌ | | Live Updates | ❌ | ✅ | | In-App Experiences | ✅ | ✅ | | Custom Views | ✅ | ✅ | | Embedded Content | ❌ | ❌ | | Message Center | ✅ | ✅ | | Preference Center | ✅ | ✅ | | Feature Flags | ✅ | ✅ | | Analytics | ✅ | ✅ | | Contacts | ✅ | ✅ | | Tags, Attributes & Subscription Lists | ✅ | ✅ | | Privacy Controls | ✅ | ✅ | ## API References * [Capacitor API Documentation](https://www.airship.com/docs/reference/libraries/capacitor-airship/latest/) ## Source * [Source](https://github.com/urbanairship/capacitor-airship) ## Changelog * [Capacitor Changelog](https://www.airship.com/docs/developer/sdk-integration/capacitor/changelog/) ## License All Airship SDKs and frameworks are open sourced and licensed under Apache Software License 2.0. * [Capacitor License](https://github.com/urbanairship/capacitor-airship/blob/main/LICENSE) ## SDK Installation Complete installation and configuration guides for the Airship Capacitor plugin. # Install and Set Up the Capacitor Plugin > How to install the Airship Capacitor plugin.## Requirements - Capacitor 5.0.0 or higher - iOS 15+ (Xcode 16+) - Android API 23+ ## Setup Install the plugin ```bash npm install @ua/capacitor-airship npx cap sync ``` ## Initialize Airship Before you can access any of the module's API, the Airship module needs to be initialized. This can be accomplished by calling `takeOff` when the device is ready with the proper config or by providing plugin config in the `capacitor.config.json` file. **Calling takeOff** ```typescript // TakeOff await Airship.takeOff({ production: { appKey: "", appSecret: "" }, development: { appKey: "", appSecret: "" }, inProduction: true, site: "us", // use "eu" for EU cloud projects urlAllowList: ["*"], android: { notificationConfig: { icon: "ic_notification", accentColor: "#00ff00" } } }) ``` **Using capacitor.config.json** ```json { "appId":"com.example.plugin", "appName":"example", "bundledWebRuntime":false, "webDir":"dist", "plugins":{ "SplashScreen":{ "launchShowDuration":0 }, "Airship":{ "config":{ "default":{ "appKey":"", "appSecret":"" }, "site":"us", "urlAllowList":[ "*" ], "android":{ "notificationConfig":{ "icon":"ic_notification", "accentColor":"#00ff00" } } } } }, "server":{ "androidScheme":"https" } } } ``` Takeoff can be called multiple times, but the config passed in `takeOff` won't be applied until the next app init. For a complete list of configuration options, see the [AirshipConfig reference](https://www.airship.com/docs/reference/libraries/capacitor-airship/latest/interfaces/AirshipConfig.html). ## Test the integration After completing the setup, verify your integration: 1. **Build and run your app** on your iOS or Android device/simulator/emulator. 2. **Check the console output** for Airship channel creation: - **iOS**: Look for a log message in Xcode console: `Channel ID: ` - **Android**: Look for a log message in logcat: `Airship channel created: ` - The channel ID confirms successful SDK initialization. - For more detailed logging, see [Logging](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/logging/). If you see the channel ID in the console and no errors, your integration is successful. ## Next steps - [Advanced Configuration](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/advanced-configuration/): Configure URL allowlists and other advanced settings - [Extending Airship](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/): Access native SDK features for advanced customization - [Push Notifications](https://www.airship.com/docs/developer/sdk-integration/capacitor/push-notifications/getting-started/): Configure push notifications - [Deep Links](https://www.airship.com/docs/developer/sdk-integration/capacitor/deep-links/): Handle deep links in your app If you don't see a channel ID or encounter errors during initialization, see [Troubleshooting Initialization](https://www.airship.com/docs/developer/sdk-integration/capacitor/troubleshooting/initialization/) for common problems and solutions. # Logging > Configure log levels and privacy settings to control how the Airship SDK logs messages. The Airship SDK provides configurable log levels to help you debug issues without overwhelming the console. By default, the log level is set to **Info** for development builds and **Error** for production builds to ensure clean logs in a live environment. ## Log levels The following log levels are available, ordered from most to least verbose. | Log Level | Description | | :-------- | :---------- | | **Verbose** | Reports highly detailed SDK status, which is useful for deep debugging and troubleshooting. | | **Debug** | Reports general SDK status with more detailed information than `Info`. | | **Info** | Reports general SDK status and lifecycle events. | | **Warning** | Used for API deprecations, invalid setup, and other potentially problematic situations that are generally recoverable. | | **Error** | Used for critical errors, exceptions, and other situations that the SDK cannot gracefully handle. | | **None** | Disables all logging. | ## Configuring log levels You can set the log level in the Airship config options that are passed during takeOff. This setting acts as a minimum, and only logs at that level and higher will be logged. ```typescript // Available log levels: verbose, debug, info, warning, none await Airship.takeOff({ default: { logLevel: "verbose" }, ... }); ``` ## Log privacy levels For better security in production environments, you can control the visibility of log contents using privacy levels. This is especially useful when you need to debug a release build without exposing sensitive information. ### private (default) This is the default setting, designed to protect data in a production environment. ### public This setting increases log visibility, making it easier to capture detailed information from release builds. To ensure visibility in production builds, `verbose` and `debug` messages are automatically elevated to the `info` log level. ## Setting the privacy level You can set the privacy level in the Airship config options that are passed during takeOff. ```typescript await Airship.takeOff({ default: { logLevel: "verbose", ios: { logPrivacyLevel: "public" }, android: { logPrivacyLevel: "public" } }, ... }); ``` # Locale > Configure locale behavior and override the default locale that Airship uses. The Airship SDK is localized in 48 different languages for all strings included within the SDK. The strings within the app will automatically use the device's or app's configured [Locale](https://www.airship.com/docs/reference/glossary/#locale). Airship uses the user's locale for various locale-sensitive tasks, including selecting the language for messages and reporting for analytics. Apps can override the locale so that Airship uses a different locale than the device's current locale for messaging. ## Overriding the locale You can override the locale programmatically at runtime, which takes precedence over the device's locale settings. ```typescript await Airship.locale.setLocaleOverride("de") ``` ## Clearing the locale override To remove a locale override and return to using the device's locale: ```typescript await Airship.locale.clearLocaleOverride() ``` ## Getting the current locale To retrieve the locale that Airship is currently using: ```typescript const locale = await Airship.locale.getLocale() ``` # Advanced Configuration > Configure advanced settings like URL allowlists and other options for the Airship Capacitor plugin. ## URL Allowlist The URL allowlist controls which URLs the Airship SDK is able to act on. Configure the URL allowlist in your `takeOff` config options using the following properties: - `urlAllowListScopeOpenUrl`: Only URLs allowed for this scope can be opened from an action, displayed in landing page, displayed in an HTML in-app message, or displayed as media in an In-App Automation. Defaults to any Airship-originated URLs and YouTube URLs. - `urlAllowListScopeJavaScriptInterface`: These URLs are checked before the Airship JavaScript interface is injected into the webview. Defaults to any Airship-originated URLs. - `urlAllowList`: Both scopes are applied to these URLs. ```typescript await Airship.takeOff({ production: { appKey: "", appSecret: "" }, inProduction: true, site: "us", urlAllowList: ["*"], // Accept all URLs // Or configure specific scopes: urlAllowListScopeOpenUrl: ["https://example.com/*", "https://*.youtube.com/*"], urlAllowListScopeJavaScriptInterface: ["https://example.com/*"] }) ``` **Valid URL pattern syntax** ```text := '*' | '://'/ | '://' | ':/' | ':///' := := '*' | '*.' | := ``` To accept any URL within the SDK, set the `urlAllowList` to `["*"]`. For a complete list of configuration options, see the [AirshipConfig reference](https://www.airship.com/docs/reference/libraries/capacitor-airship/latest/interfaces/AirshipConfig.html). # Extend Airship > How to extend the Airship Capacitor plugin to access native iOS and Android SDK features not exposed through the Capacitor API. You can provide a plugin extender that will be automatically loaded for the app. The extender can be used to modify the Airship config before SDK initialization and to access the underlying native SDK once Airship is ready. This gives the app a chance to customize parts of Airship that are not configurable through the Capacitor plugin, such as setting up [iOS Live Activities](https://www.airship.com/docs/developer/sdk-integration/capacitor/live-activities/) and [Android Live Updates](https://www.airship.com/docs/developer/sdk-integration/capacitor/live-updates/). ## iOS For iOS, create a Swift file named `AirshipPluginExtender.swift` and needs to be included in the main app target. Make sure the class has the `@objc(AirshipPluginExtender)` annotation and inherits `AirshipPluginExtenderProtocol`. ```swift import Foundation import AirshipKit import AirshipFrameworkProxy import ActivityKit @objc(AirshipPluginExtender) public class AirshipPluginExtender: NSObject, AirshipPluginExtenderProtocol { public static func onAirshipReady() { // Called when Airship is ready on the MainActor } public static func extendConfig(config: inout AirshipConfig) { // Called to extend the AirshipConfig before SDK initialization } } ``` ## Android Create a file in the App's src directory named `AirshipExtender`. It needs to extend `com.urbanairship.android.framework.proxy.AirshipPluginExtender` and have an empty constructor. ```kotlin // Replace with your package package com.example import android.content.Context import androidx.annotation.Keep import com.urbanairship.AirshipConfigOptions import com.urbanairship.UAirship import com.urbanairship.android.framework.proxy.AirshipPluginExtender @Keep public final class AirshipExtender: AirshipPluginExtender { override fun onAirshipReady(context: Context, airship: UAirship) { // Called when Airship is ready on a background thread. // Avoid doing long running, blocking work or it will delay Airship } override fun extendConfig( context: Context, configBuilder: AirshipConfigOptions.Builder ): AirshipConfigOptions.Builder { // Called to extend the AirshipConfig before SDK initialization return configBuilder } } ``` Register the extender in the manifest: ```xml ``` ## Push Notifications Configure and implement push notifications for iOS and Android platforms. # Push Notifications > How to configure your application to receive and respond to notifications. ## Platform Setup Before you can send and receive push notifications, you need to configure your app for the platform(s) you're targeting. Follow the platform-specific setup instructions below. ### iOS #### Enable Push Notifications Capability 1. Open your project in Xcode. 2. Click on your project in the Project Navigator. 3. Select your main app target and then click the **Signing & Capabilities** tab. 4. If you do not see Push Notifications enabled, click **+ Capability** and add **Push Notifications**. ![Adding the Push Notifications capability in Xcode](https://www.airship.com/docs/images/ios-enable-push-notifications-capabilities_hu_2e1789fffb02612b.webp) *Adding the Push Notifications capability in Xcode* #### Enable Background Modes 1. Select your main app target and then click the **Signing & Capabilities** tab. 2. Click **+ Capability** and add **Background Modes**. ![Adding the Background Modes capability in Xcode](https://www.airship.com/docs/images/ios-enable-background-mode-capabilities_hu_f135d9fec0ba0d06.webp) *Adding the Background Modes capability in Xcode* 3. In the **Background Modes** section, select the **Remote notifications** checkbox. ![Enabling Remote notifications in Background Modes](https://www.airship.com/docs/images/ios-background-mode-remote-notifications_hu_7e38b08288fcd7b2.webp) *Enabling Remote notifications in Background Modes* #### Notification Service Extension

To take advantage of notification attachments, such as images, animated gifs, and video, you will need to create a notification service extension.

Follow the steps in the [iOS Notification Service Extension Guide](https://www.airship.com/docs/developer/sdk-integration/apple/push-notifications/notification-service-extension/). ### Android Configure Firebase Cloud Messaging (FCM) or Huawei Mobile Services (HMS) to enable push notifications on Android. #### FCM Setup Follow the [Android FCM setup instructions](https://www.airship.com/docs/developer/sdk-integration/android/installation/getting-started/#fcm-setup). #### HMS Setup 1. Follow [Huawei's documentation](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-integrating-sdk-0000001050040084) to set up the HMS SDK. > **Note:** Airship requires HMS Core Push SDK 6.3.0.304 or newer. 2. Add `airshipHmsEnabled=true` to the app's gradle.properties. #### Notification Configuration Configure the notification icon and accent color in your `takeOff` config: ```typescript await Airship.takeOff({ production: { appKey: "", appSecret: "" }, development: { appKey: "", appSecret: "" }, inProduction: true, site: "us", android: { notificationConfig: { icon: "ic_notification", accentColor: "#00ff00" } } }) ``` See the [Capacitor Plugin Setup guide](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/) for complete `takeOff` configuration options. ## Enable User Notifications The Airship SDK distinguishes between *user notifications* (visible to users) and *silent push notifications* (background data delivery). User notifications require explicit permission from the user. By default, user notifications are disabled. Enable them when you want to show visible notifications to users. ### Basic Enablement The simplest way to enable user notifications is with `setUserNotificationsEnabled()`: ```typescript await Airship.push.setUserNotificationsEnabled(true) ``` This will prompt the user for permission if not already granted. However, it does not provide feedback on whether the user accepted or denied the permission. > **Note:** For apps that target Android 13 (API 33) and above, enabling user notifications will display a runtime permission prompt. > > To increase the likelihood that the user will accept, you should avoid prompting the user for permission immediately on app startup, and instead wait for a more appropriate time to prompt for notification permission. ### Async Enablement with Fallback For more control over the permission flow, use `enableUserNotifications()` which returns the permission result and supports a fallback option: ```typescript // Enable with system settings fallback const granted = await Airship.push.enableUserNotifications({ fallback: "systemSettings" }) if (granted) { console.log('Notifications enabled') } else { console.log('Notifications denied') } ``` The `systemSettings` fallback option will prompt the user to open system settings if permission is denied on iOS or denied silently on Android. This gives users a second chance to enable notifications if they initially declined. ### Checking Notification Status To check if user notifications are currently enabled: ```typescript const enabled = await Airship.push.isUserNotificationsEnabled() ``` For more detailed status information, use `getNotificationStatus()`: ```typescript const status = await Airship.push.getNotificationStatus() console.log('Are notifications allowed:', status.areNotificationsAllowed) console.log('Is opted in:', status.isOptedIn) ``` To monitor notification status changes in real-time: ```typescript const handle = await Airship.push.onNotificationStatusChanged(event => { console.log('Notification status changed:', event.status) console.log('Is opted in:', event.status.isOptedIn) }) ``` ### Getting the Push Token To get the platform-specific push token (APNs token on iOS, FCM token on Android): ```typescript const token = await Airship.push.getPushToken() // Or listen for token updates const handle = await Airship.push.onPushTokenReceived(event => { console.log('Push token:', event.pushToken) }) ``` If push notifications aren't working as expected, see [Troubleshooting Push Notifications](https://www.airship.com/docs/developer/sdk-integration/capacitor/troubleshooting/push-notifications/) to check notification status and fix common issues. # Notification Events > How to handle push notification events, respond to user interactions, and manage active notifications. The Airship SDK provides event listeners for when a push is received or a notification is interacted with. ## Push Received Listen for when a push notification is received: ```typescript const handle = await Airship.push.onPushReceived(event => { console.log('Push received:', event.pushPayload) }) ``` This event fires when a push notification arrives, regardless of whether the app is in the foreground or background. ## Notification Response Listen for when a user interacts with a notification: ```typescript const handle = await Airship.push.onNotificationResponse(event => { console.log('Notification tapped:', event) console.log('Action ID:', event.actionId) if (event.actionId === 'custom_action') { // Handle custom action } }) ``` This event fires when a user taps on a notification or a notification action button. ## Managing Active Notifications You can retrieve and clear notifications that are currently displayed in the notification center. ### Get Active Notifications Retrieve the list of currently displayed notifications: ```typescript const notifications = await Airship.push.getActiveNotifications() console.log('Active notifications:', notifications) ``` > **Note:** On Android, this list only includes notifications sent through Airship. ### Clear Notifications Clear all notifications for the app: ```typescript await Airship.push.clearNotifications() ``` Clear a specific notification by identifier: ```typescript await Airship.push.clearNotification(identifier) ``` > **Note:** On Android, you can use this method to clear notifications outside of Airship. The identifier is in the format `:`. # Customize Notifications > How to customize push notification presentation, badges, quiet time, and foreground display behavior. ## iOS Notification Options By default, the Airship SDK will request `alert`, `badge`, and `sound` notification options for remote notifications. This can be configured by setting notification options before enabling user notifications. ```typescript await Airship.push.iOS.setNotificationOptions([ "alert", "badge", "sound" ]) ``` ### Provisional Authorization Apps can request provisional authorization along with the usual notification options. When requesting provisional authorization, apps do not need to prompt the user for permission initially, and notifications will be delivered in a non-interruptive manner to the Notification Center until the user explicitly chooses to keep delivering messages either prominently or quietly. ```typescript await Airship.push.iOS.setNotificationOptions([ "alert", "badge", "sound", "provisional" ]) ``` ### Foreground Presentation Options When a push is received in the foreground on iOS, how the notification is displayed to the user is controlled by foreground presentation options. By default, the SDK will not set any options so the notification will be silenced. ```typescript await Airship.push.iOS.setForegroundPresentationOptions([ "banner", "list", "sound" ]) ``` ### Badges The badge on iOS presents a counter on top of the application icon. You can control this directly through Airship. ```typescript // Set badge number await Airship.push.iOS.setBadgeNumber(20) // Reset badge await Airship.push.iOS.setBadgeNumber(0) // Enable auto-badge await Airship.push.iOS.setAutobadgeEnabled(true) ``` > **Important:** When using auto-badge, only modify the badge value through Airship methods to ensure the value stays in sync. ### Quiet Time Quiet time allows you to suppress notifications during specific hours. Notifications are still received but won't be displayed to the user during the quiet time window. ```typescript // Set quiet time hours await Airship.push.iOS.setQuietTime({ startHour: 22, startMinute: 0, endHour: 7, endMinute: 0 }) // Enable quiet time await Airship.push.iOS.setQuietTimeEnabled(true) ``` > **Note:** Quiet time is only supported on iOS. ## Android Foreground Notifications By default, push notifications received while the app is in the foreground on Android are displayed to the user. You can control this behavior globally: ```typescript // Disable foreground notifications await Airship.push.android.setForegroundNotificationsEnabled(false) // Enable foreground notifications await Airship.push.android.setForegroundNotificationsEnabled(true) ``` ## Silent Notifications Silent notifications are push messages that do not present a notification to the user. These are typically used to briefly wake the app from a background state to perform processing tasks or fetch remote content. > **Important:** We recommend that you thoroughly test your implementation to confirm that silent notifications do not generate any device notifications. For Android, all push messages are delivered in the background, but default Airship will treat messages without an `alert` as silent. For iOS, set the `content_available` property to `true` in the [iOS override object](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#iosoverrideobject). > **Note:** Pushes sent with the `content_available` property (iOS) or without an `alert` (Android) do not have guaranteed delivery. Factors affecting delivery include battery life, whether the device is connected to WiFi, and the number of silent pushes sent within a recent time period. These metrics are determined solely by iOS/Android and APNs/FCM. Therefore, this feature is best used for supplementing the regular behavior of the app rather than providing critical functionality. For instance, an app could use a silent push to pre-fetch new data ahead of time in order to reduce load times when the app is later launched by the user. ## In-App Experiences Configure and control In-App Experiences in Capacitor applications. # In-App Experiences > Pause, resume, and control display timing for In-App Experiences. In-App Experiences are automatically enabled when you integrate the Airship SDK. Use these methods to control when and how they are displayed. ## Pausing and Resuming Display You can pause and resume In-App Experiences to control when they are displayed to users. ```typescript // Pause in-app experiences await Airship.inApp.setPaused(true) // Resume in-app experiences await Airship.inApp.setPaused(false) // Check if paused const isPaused = await Airship.inApp.isPaused() ``` ### Auto-Pause on Launch You can configure the SDK to automatically pause In-App Experiences on launch. This is useful if you want to defer showing In-App Experiences until after onboarding or other critical app flows. ```typescript await Airship.takeOff({ production: { appKey: "", appSecret: "" }, development: { appKey: "", appSecret: "" }, inProduction: true, site: "us", autoPauseInAppAutomationOnLaunch: true }) ``` See the [Capacitor Plugin Setup guide](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/) for complete `takeOff` configuration options. When you're ready to display In-App Experiences, call `setPaused(false)`: ```typescript await Airship.inApp.setPaused(false) ``` ## Display Interval Control the minimum time between In-App Experience displays to avoid overwhelming users. ```typescript // Set display interval to 5 seconds await Airship.inApp.setDisplayInterval(5000) // Get current display interval const interval = await Airship.inApp.getDisplayInterval() ``` The display interval is the minimum time (in milliseconds) that must pass between displaying In-App Experiences. # Custom Views > Register custom native views to use within Scenes. A *Custom View* is a native view from your mobile or web application embedded into a Scene. Custom Views can display any native content your app exposes, so you can reuse that existing content within any screen in a Scene. Custom Views allow you to embed native iOS and Android views within Scenes, giving you full control over design and layout while leveraging Airship's targeting and orchestration capabilities. ## Requirements To use Custom Views in Capacitor, you must extend the native Airship modules using the `AirshipPluginExtender`. See [Extending Airship](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/) for setup instructions. ## Registering Custom Views Custom Views must be registered on each native platform separately: ### iOS See the [Apple Custom Views documentation](https://www.airship.com/docs/developer/sdk-integration/apple/in-app-experiences/custom-views/) for detailed implementation instructions. Custom Views should be registered after takeOff in your native iOS code: ```swift import AirshipKit @objc(AirshipExtender) class AirshipExtender: NSObject, AirshipPluginExtenderDelegate { func onAirshipReady() { // Register custom views AirshipCustomViewManager.shared.register(name: "my-custom-view") { args in // Return your SwiftUI view MyCustomView(args: args) } } } ``` ### Android See the [Android Custom Views documentation](https://www.airship.com/docs/developer/sdk-integration/android/in-app-experiences/custom-views/) for detailed implementation instructions. Custom Views should be registered during the `onAirshipReady` callback in your native Android code: ```kotlin import com.urbanairship.AirshipCustomViewManager class AirshipExtender : AirshipPluginExtender { override fun onAirshipReady(context: Context) { // Register custom views AirshipCustomViewManager.register("my-custom-view") { context, args -> // Return your Android View MyCustomView(context, args) } } } ``` ## Using Custom Views Once registered, Custom Views can be added to Scenes in the Airship dashboard: 1. Create or edit a Scene 2. Add the **Custom View** content element to a screen 3. Enter the view name (e.g., `my-custom-view`) that matches the name you registered in your native code 4. Optionally add key-value pairs to pass custom properties to the view The native view will be displayed within the Scene with the properties you configured. ## Example: Embedding Capacitor Web Views A powerful use case for Custom Views in Capacitor is embedding your Capacitor web app (or a subset of it) directly within a Scene. This allows you to display specific routes or pages from your web app inside an Airship Scene. ### iOS Implementation ```swift import Foundation import AirshipFrameworkProxy import Capacitor import AirshipCore import SwiftUI import UIKit @objc(AirshipPluginExtender) public class AirshipPluginExtender: NSObject, AirshipPluginExtenderProtocol { @MainActor public static func onAirshipReady() { AirshipCustomViewManager.shared.register(name: "capacitor") { args in CapView( startPath: args.properties?.object?["path"]?.string ) .edgesIgnoringSafeArea(.all) .frame(maxWidth: .infinity, maxHeight: .infinity) } } } struct CapView: UIViewControllerRepresentable { let startPath: String? func makeUIViewController(context: Context) -> CustomCapViewController { let controller = CustomCapViewController() controller.startPath = startPath return controller } func updateUIViewController(_ uiViewController: CustomCapViewController, context: Context) { // Handle updates (if necessary) } class CustomCapViewController: CAPBridgeViewController { var startPath: String? = nil override func instanceDescriptor() -> InstanceDescriptor { let descriptor = super.instanceDescriptor() descriptor.appStartPath = startPath return descriptor } } } ``` ### Android Implementation First, create a layout file at `res/layout/custom_view.xml`: ```xml ``` Then, register the custom view in your `AirshipExtender.kt`: ```kotlin package com.example.plugin import android.content.Context import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import androidx.annotation.Keep import androidx.appcompat.app.AppCompatActivity import androidx.core.view.doOnAttach import androidx.fragment.app.Fragment import com.getcapacitor.Bridge import com.getcapacitor.CapConfig import com.getcapacitor.Logger import com.getcapacitor.PluginLoadException import com.getcapacitor.PluginManager import com.urbanairship.UAirship import com.urbanairship.android.framework.proxy.AirshipPluginExtender import com.urbanairship.android.layout.AirshipCustomViewArguments import com.urbanairship.android.layout.AirshipCustomViewHandler import com.urbanairship.android.layout.AirshipCustomViewManager import com.urbanairship.json.optionalField @Keep class AirshipExtender: AirshipPluginExtender { override fun onAirshipReady(context: Context, airship: UAirship) { AirshipCustomViewManager.register("capacitor", CustomCapViewHandler()) } } class CustomCapViewHandler: AirshipCustomViewHandler { override fun onCreateView(context: Context, args: AirshipCustomViewArguments): View { return CapView(context).also { it.startPath = args.properties.optionalField("path") } } } class CapView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { var startPath: String? = null init { id = View.generateViewId() val fm = (context as AppCompatActivity).supportFragmentManager fm.findFragmentById(id)?.let { fm.beginTransaction() .remove(it) .commit() } doOnAttach { findViewById(id)?.let { fm.beginTransaction() .setReorderingAllowed(true) .add(id, CapFragment.newInstance(startPath)) .commitNowAllowingStateLoss() } } } } class CapFragment: Fragment() { private var bridge: Bridge? = null private val path: String? by lazy { arguments?.getString(ARG_PATH) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.custom_view, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) load(savedInstanceState) } override fun onStart() { super.onStart() bridge?.onStart() } override fun onResume() { super.onResume() bridge?.app?.fireStatusChange(true) bridge?.onResume() } override fun onPause() { super.onPause() bridge?.app?.fireStatusChange(false) bridge?.onPause() } override fun onStop() { super.onStop() bridge?.onStop() } override fun onDestroy() { super.onDestroy() bridge?.onDestroy() bridge?.onDetachedFromWindow() } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) bridge?.onConfigurationChanged(newConfig) } private fun load(savedInstanceState: Bundle?) { if (bridge == null) { bridge = Bridge.Builder(this) .apply { try { val loader = PluginManager(requireActivity().assets) addPlugins(loader.loadPluginClasses()) } catch (ex: PluginLoadException) { Logger.error("Error loading plugins.", ex) } } .setConfig( CapConfig.Builder(context) .setStartPath(this.path) .create() ) .setInstanceState(savedInstanceState) .create() } } companion object { const val ARG_PATH: String = "path" @JvmStatic @JvmOverloads fun newInstance(path: String? = null): CapFragment = CapFragment().apply { arguments = Bundle().apply { path?.let { putString(ARG_PATH, it) } } } } } ``` ### Usage in Scenes When creating a Scene in the Airship dashboard: 1. Add a **Custom View** content element 2. Set the view name to `capacitor` 3. Optionally add a `path` property to specify which route in your Capacitor app to display (e.g., `/products`, `/settings`) > **Note:** **Sizing limitation**: The custom view must use hard-coded sizing (percentages or pixels). Auto-sizing is not currently supported for embedded Capacitor views. This implementation creates a fully functional Capacitor web view within the Scene, complete with all your Capacitor plugins and routing capabilities. The optional `path` property allows you to display specific routes or pages from your web app. ## Message Center Implement Message Center to provide an inbox for rich HTML-based messages. # Message Center > The default Message Center is available for Capacitor with minimal integration required. Basic theming options are supported. Message Center provides an inbox for rich, HTML-based messages. Learn more about Message Center in our [feature guide](https://www.airship.com/docs/guides/features/messaging/message-center/). ## Display the Message Center Display the Message Center with a single method call: ```js await Airship.messageCenter.display() ``` To build a custom message list, see [Embedding the Message Center](https://www.airship.com/docs/developer/sdk-integration/capacitor/message-center/embedding/). Individual messages will still display as a native overlay. ## Fetch Messages Retrieve messages from the inbox: ```js const messages = await Airship.messageCenter.getMessages() ``` ## Listen for Message Updates Subscribe to message updates using event listeners: ```js Airship.addListener('messageCenterUpdated', async () => { const messages = await Airship.messageCenter.getMessages(); // Handle messages }); ``` ## Listen for Unread Count Changes Subscribe to unread count updates: ```js const unreadCount = await Airship.messageCenter.getUnreadCount(); // Update badge or UI ``` ## Refresh Messages Manually refresh the message list from the server: ```js await Airship.messageCenter.refreshMessages() ``` ## Mark Messages as Read Mark one or more messages as read: ```js await Airship.messageCenter.markMessageRead("message-id") ``` ## Delete Messages Delete one or more messages: ```js await Airship.messageCenter.deleteMessage("message-id") ``` # Embed the Message Center > Create custom Message Center lists with full control over design and navigation. This guide covers creating custom Message Center list implementations for Capacitor applications. You can build a custom message list using web technologies, but individual messages must be displayed using the native overlay. > **Note:** Capacitor does not support embedding native message views. Use `showMessageView()` to display individual messages as an overlay. ## Override Default Display Behavior To use a custom Message Center list instead of the default UI, disable auto-launch and add a listener to handle display events: ```js // Disable the default UI await Airship.messageCenter.setAutoLaunchDefaultMessageCenter(false) // Add a listener to handle display events Airship.messageCenter.onDisplay(event => { if (event.messageId) { // Show specific message in native overlay await Airship.messageCenter.showMessageView(event.messageId); } else { // Navigate to your custom message list navigateToCustomMessageList(); } }); ``` ## Displaying Individual Messages Use `showMessageView` to display messages in a native overlay: ```js await Airship.messageCenter.showMessageView("message-id") ``` ## Preference Center Implement Preference Center to let users control their subscription preferences. # Preference Center > Preference Center allows users to opt in and out of subscription lists configured via the Airship Dashboard. > **Important:** Airship Preference Centers are widgets that can be embedded in a page in an app or website. Please verify with your legal team that your full Preference Center page, including any web page for email Preference Centers, is compliant with local privacy regulations. Preference Center provides a pre-built UI for users to manage their subscription preferences. Learn more in the [Preference Center user guide](https://www.airship.com/docs/guides/messaging/features/preference-centers/). ## Display a Preference Center Display a Preference Center with a single method call: ```js await Airship.preferenceCenter.display("preference-center-id") ``` To build a custom Preference Center UI, see [Embedding the Preference Center](https://www.airship.com/docs/developer/sdk-integration/capacitor/preference-center/embedding/). # Embed the Preference Center > Create custom Preference Center UIs by fetching the config and building your own subscription management interface. This guide covers creating custom Preference Center UIs for Capacitor applications. Unlike the default Preference Center, you'll build your own UI from scratch using the Preference Center configuration and subscription list APIs. ## Override Default Display Behavior To use a custom Preference Center instead of the default UI, disable auto-launch for the specific Preference Center ID and handle display events: ```js // Disable the OOTB UI for this Preference Center await Airship.preferenceCenter.setAutoLaunchDefaultPreferenceCenter( "preference-center-id", false ); // Add a listener to handle display events Airship.preferenceCenter.onDisplay(event => { const preferenceCenterId = event.preferenceCenterId; // Navigate to your custom preference center UI navigateToCustomPreferenceCenter(preferenceCenterId); }); ``` ## Fetching Preference Center Config The Preference Center config contains all the information needed to build your UI, including subscription lists, sections, and display settings. ```js const config = await Airship.preferenceCenter.getConfig("preference-center-id"); ``` > **Note:** The config might not be available immediately on first app start. Implement exponential backoff if automatically retrying, or provide a UI for users to manually retry. ## Building Your Custom UI You'll need to: 1. **Fetch the config** to get the list of subscription lists and their current state 2. **Build your UI** using the config data (sections, subscription lists, display settings) 3. **Update subscription lists** when users make changes using the [Subscription List APIs](https://www.airship.com/docs/developer/sdk-integration/capacitor/audience/subscription-lists/) ### Example Implementation ```js async function loadPreferenceCenter(preferenceCenterId) { try { const config = await Airship.preferenceCenter.getConfig(preferenceCenterId); // Build your UI with the config const container = document.getElementById('preference-center'); // Display title const title = document.createElement('h1'); title.textContent = config.display.name; container.appendChild(title); // Display sections config.sections.forEach((section) => { const sectionDiv = document.createElement('div'); const sectionTitle = document.createElement('h2'); sectionTitle.textContent = section.display.name; sectionDiv.appendChild(sectionTitle); // Display items (subscription lists) section.items.forEach((item) => { const itemDiv = document.createElement('div'); const label = document.createElement('label'); label.textContent = item.display.name; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = await isSubscribed(item.subscriptionId); checkbox.addEventListener('change', async (e) => { if (e.target.checked) { await Airship.contact.subscriptionLists.subscribe(item.subscriptionId); } else { await Airship.contact.subscriptionLists.unsubscribe(item.subscriptionId); } }); itemDiv.appendChild(checkbox); itemDiv.appendChild(label); sectionDiv.appendChild(itemDiv); }); container.appendChild(sectionDiv); }); } catch (error) { console.error('Failed to load Preference Center:', error); } } ``` > **Important:** Preference Center configuration is currently limited to subscription lists only. Use the [Subscription List APIs](https://www.airship.com/docs/developer/sdk-integration/capacitor/audience/subscription-lists/) to manage user subscriptions. ## Audience Management Integrate audience management features into your Capacitor app. This guide covers how to identify contacts, access channel IDs, and set tags, attributes, and subscription lists on channels and contacts. For information about using these features for segmentation and targeting, see the [Audience User Guide]({{< ref "/guides/audience/segmentation/segmentation.md" >}}). # Channels > Access and manage channel IDs and listen for channel creation. Each device/app install will generate a unique identifier known as the Channel ID. Once a Channel ID is created, it will persist in the application until the app is reinstalled, or has its internal data is cleared. For information about finding Channel IDs, using the Channel Capture tool, and other methods to access Channel IDs, see [Finding Channel IDs](https://www.airship.com/docs/guides/getting-started/developers/identifiers/). ## Accessing the Airship Channel ID Apps can access the Channel ID directly through the SDK. ```js // Get the channel ID (may return null if not yet created) const channelID = await Airship.channel.getChannelId() // Wait for the channel ID to be created (returns the channel ID once available) const channelID = await Airship.channel.waitForChannelId() ``` The Channel ID is asynchronously created, so it may not be available right away on the first run. Use `waitForChannelId()` if you need to wait for the channel to be created before proceeding. Changes to Channel data will automatically be batched and applied when the Channel is created, so there is no need to wait for the Channel to be available before modifying any data. Applications that need to access the Channel ID can use a listener to be notified when it is available. ```js await Airship.channel.onChannelCreated(event => { console.log('Channel created: ' + event.channelId); }); ``` ## Channel Capture tool The Channel Capture tool is a feature built into the SDK that helps users find their Channel ID. For detailed information about how it works and how to use it, see [Finding Channel IDs](https://www.airship.com/docs/guides/getting-started/developers/identifiers/). The Channel Capture tool can be disabled through the Airship Config options passed to `takeOff` during SDK initialization. For information about setting up the Airship SDK and configuring the takeOff options, see [Capacitor SDK Setup](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/). ```js await Airship.takeoff({ ... isChannelCaptureEnabled: false, }) ``` ## Delaying channel creation Airship creates the channel if at least one feature is enabled in the Privacy Manager. To delay channel creation, use the Privacy Manager to disable all features during takeOff. For more information about Privacy Manager, see [SDK Data Collection](https://www.airship.com/docs/reference/data-collection/sdk-data-collection/). # Contacts > Identify contacts, reset contacts, and get named user IDs. A Contact is any user in your project. Contacts are identified as either an Anonymous Contact or a Named User. Airship can set targeting data on these identifiers, which are also used to map devices and channels to a specific user. For detailed information about contacts and named users, see [Named users](https://www.airship.com/docs/guides/audience/named-users/). ## Managing the Contact's identifier (Named User ID) Identify can be called multiple times with the same Named User ID. The SDK will automatically deduplicate `identify` calls made with the same Named User ID. If the ID is changed from a previous value, the Contact will automatically be dissociated from the previous Named User ID. ```js await Airship.contact.identify(namedUserId) ``` If the user logs out of the device, you may want to reset the contact. This will clear any anonymous data and dissociate the contact from the Named User ID, if set. This should only be called when the user manually logs out of the app, otherwise you will not be able to target the Channel by its Contact data. ```js await Airship.contact.reset() ``` You can get the Named User ID only if you set it through the SDK. ```js const namedUser = await Airship.contact.getNamedUserId() ``` # Tags > Set device tags, contact tags, and tag groups for audience segmentation. For information about tags, including how to use them for segmentation and targeting, see the [Tags user guide](https://www.airship.com/docs/guides/audience/tags/). ## Channel Tags Channel tags are tags managed on the Channel by the SDK. Device tags (tags without a group) can be modified or fetched from the Channel. ```js Airship.channel.editTags() .addTags(["one", "two", "three"]) .removeTags(["some_tag"]) .apply() // Accessing channel tags const tags = await Airship.channel.getTags() ``` ## Channel Tag Groups Tag groups are tags scoped within a group. Tag groups can be modified from the SDK but cannot be fetched. Device tags (tags without a group) can be fetched. If you need to be able to fetch tag groups, consider using subscription lists. ```js Airship.channel.editTagGroups() .addTags("loyalty", ["silver-member"]) .removeTags("loyalty", ["bronze-member"]) .apply() ``` ## Contact Tag Groups Contact tag groups are tags scoped within a group at the Contact level. Tag groups can be modified from the SDK but cannot be fetched. If you need to be able to fetch tag groups, consider using subscription lists. ```js Airship.contact.editTagGroups() .addTags("loyalty", ["silver-member"]) .removeTags("loyalty", ["bronze-member"]) .apply() ``` ## Verifying Tags To verify that tags have been set correctly, look up the channel or contact in the [Contact Management](https://www.airship.com/docs/guides/audience/contact-management/) view. You can search by Channel ID or Named User ID to view the tags and tag groups associated with a channel or contact. # Attributes > Set channel and contact attributes as key-value pairs for personalization. For information about Attributes, including overview, use cases, and how to target Attributes, see [About Attributes](https://www.airship.com/docs/guides/audience/attributes/about/). ## Channel Attributes Channel attributes are attributes managed on the Channel by the SDK. ```js Airship.channel.editAttributes() .setAttribute("device_name", "Bobby's Phone") .setAttribute("average_rating", 4.99) .removeAttribute("vip_status") .apply() ``` ## Contact Attributes Contact attributes are attributes managed on the Contact by the SDK. ```js Airship.contact.editAttributes() .setAttribute("first_name", "Bobby") .apply() ``` ## JSON Attributes JSON Attributes are data objects containing one or more string, number, date, or boolean key-value pairs. ```js Airship.contact.editAttributes() .setJsonAttribute("attribute_name", "instance_id", {"key":"value", "another_key":"another_value"}) .removeJsonAttribute("some_attribute_name", "some_instance_id") .apply() ``` ## Verifying Attributes To verify that attributes have been set correctly, look up the channel or contact in the [Contact Management](https://www.airship.com/docs/guides/audience/contact-management/) view. You can search by Channel ID or Named User ID to view the attributes associated with a channel or contact. # Subscription Lists > Manage channel and contact subscription lists for topic-based messaging. For information about Subscription Lists, including overview, use cases, and how to create subscription lists, see [Subscription Lists](https://www.airship.com/docs/guides/audience/segmentation/audience-lists/subscription/). ## Channel Subscription Lists Channel subscriptions apply only to the single channel. ```js // Modifying channel subscription lists Airship.channel.editSubscriptionLists() .subscribe("food") .unsubscribe("sports") .apply() // Fetching channel subscription lists const channelSubscriptions = await Airship.channel.getSubscriptionLists() ``` ## Contact Subscription Lists Contact subscriptions are set at the user-level and require a Channel scope specifying the types that the subscription list applies to. ```js // Modifying contact subscription lists Airship.contact.editSubscriptionLists() .subscribe("food", "app") .unsubscribe("sports", "sms") .apply() // Fetching contact subscription lists const contactSubscriptions = await Airship.contact.getSubscriptionLists() ``` ## Verifying Subscription Lists To verify that subscription lists have been set correctly, look up the channel or contact in the [Contact Management](https://www.airship.com/docs/guides/audience/contact-management/) view. You can search by Channel ID or Named User ID to view the subscription lists associated with a channel or contact. ## Data Collection Overview of data collection and controls provided by the Airship Capacitor SDK. # Privacy Manager > Use Privacy Manager to enable or disable Airship SDK features for privacy and consent management. Privacy Manager allows you to control which Airship SDK features are enabled. This is particularly useful for consent opt-in flows where you need to disable all features initially, then enable them as users grant consent. For information about what data is collected for each Privacy Manager flag, see [SDK Data Collection](https://www.airship.com/docs/reference/data-collection/sdk-data-collection/). When all features are disabled, the SDK operates in a no-op mode—it doesn't store data or make network requests. Once features are enabled, you can enable or disable specific features at runtime based on user consent. ## Privacy Manager flags Each Privacy Manager flag controls a group of related Airship features. Enabling a flag enables all features within that group: | Privacy Manager Flag | Features | |----------------------|----------| | `push` | Push notifications | | `in_app_automation` | In-App Automation, In-App Messages, Scenes, and Landing Pages | | `message_center` | Message Center | | `tags_and_attributes` | [Tags](https://www.airship.com/docs/guides/audience/tags/), [Attributes](https://www.airship.com/docs/guides/audience/attributes/about/), Subscription Lists, and Preference Center | | `contacts` | Contact Tags, Attributes, and Subscription Lists; Named User; and Associated Channels | | `analytics` | Associated identifiers, Custom events, Screen tracking, Surveys, email address, Feature Flag interaction | | `feature_flags` | Feature Flag evaluation and interaction | | `all` | All features | | `none` | No features | ## Configuring default enabled features You can configure which features are enabled by default when the SDK initializes. This is done in your Airship config during `takeOff`. For information about setting up the Airship SDK and configuring the takeOff options, see [Capacitor SDK Setup](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/). ### Default configuration (all features enabled) By default, all features are enabled. The SDK will collect data and make network requests as configured. ### Disabling all features To disable all features by default (useful for consent opt-in flows), set the enabled features to an empty array: ```typescript await Airship.takeOff({ default: { enabledFeatures: [] }, ... }); ``` ### Enabling specific features To enable only specific features by default: ```typescript await Airship.takeOff({ default: { enabledFeatures: ["push", "analytics"] }, ... }); ``` ## Modifying enabled features at runtime You can enable or disable features at any time after takeOff. Once you modify the enabled features from the default, those settings are persisted between app launches. ### Enabling features ```typescript await Airship.privacyManager.enableFeatures(["push", "analytics"]); ``` ### Disabling features ```typescript await Airship.privacyManager.disableFeatures(["push", "analytics"]); ``` ## Consent opt-in flow example A common use case is to start with all features disabled, then enable them as users grant consent: ```typescript // Start with all features disabled await Airship.takeOff({ default: { enabledFeatures: [] }, ... }); // Later, when user grants consent: async function userGrantedConsent() { // Enable features based on user's consent choices await Airship.privacyManager.enableFeatures(["push", "analytics"]); } ``` > **Note:** If features are disabled after being previously enabled, the SDK may make a few network requests to opt the channel out to prevent notifications. ## Related documentation - [SDK Data Collection](https://www.airship.com/docs/reference/data-collection/sdk-data-collection/) - Comprehensive overview of what data Airship collects for each Privacy Manager flag - [Apple Privacy Manifest](https://www.airship.com/docs/reference/data-collection/apple-privacy-manifest/) - Declare data collection practices to Apple (iOS) - [Google Play Data Safety](https://www.airship.com/docs/reference/data-collection/google-play-data-safety/) - Reference for Google Play's Data Safety section (Android) - [Analytics](https://www.airship.com/docs/developer/sdk-integration/capacitor/data-collection/analytics/) - Track user engagement with custom events, screen tracking, and associated identifiers # Analytics > Track user engagement and app performance with Airship analytics, including custom events, screen tracking, and associated identifiers. For information about controlling what data Airship collects, see [Privacy Manager](https://www.airship.com/docs/developer/sdk-integration/capacitor/data-collection/privacy-manager/). > **Note:** Analytics events are batched and uploaded asynchronously in the background to minimize battery impact. The database size is fixed, so events are safely stored even when offline. Events may not upload immediately and may wait until the next app initialization if the app is closed before the upload completes. ## Custom Events Track user activities and key conversions with custom events. They require enabling analytics for your app. For detailed information, see the [Custom Events guide](https://www.airship.com/docs/guides/audience/events/custom-events/). ```typescript let event = { eventName: "event_name", eventValue: 123.12, properties: { "my_custom_property": "some custom value", "is_neat": true, "any_json": { "foo": "bar" } } } await Airship.analytics.addCustomEvent(event) ``` ## Associated Identifiers Associated identifiers (also called custom identifiers) associate an external identifier with a [Channel ID](https://www.airship.com/docs/reference/glossary/#channel_id). They are visible in [Real-Time Data Streaming](https://www.airship.com/docs/reference/glossary/#rtds). We recommend adding any IDs that you may want to be visible in your event stream. You can assign up to 20 associated identifiers to a device. Unlike other identifiers (e.g., tags), you cannot use associated identifiers to target your users. ```typescript await Airship.analytics.associateIdentifier("key", "value") ``` ## Screen Tracking The Airship SDK gives you the ability to track which screens a user views within the application, how long a user stayed on each screen, and also includes the user's previous screen. These events then come through [Real-Time Data Streaming](https://www.airship.com/docs/reference/glossary/#rtds), allowing you to see the path a user took through the application, or trigger actions based on a user visiting a particular area of the application. ```typescript await Airship.analytics.trackScreen("MainScreen") ``` ## Troubleshooting Common issues and solutions for Airship plugin setup, initialization, and integration. # Troubleshooting Initialization > Troubleshoot common initialization issues and apply solutions. When following steps in [Getting Started](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/) or [Advanced Configuration](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/advanced-configuration/), if you don't see a channel ID in the logs or encounter errors during initialization, review the following common problems and solutions. ## Installation errors If you encounter errors during installation: - Verify that you're using a compatible version of Capacitor. See [Requirements](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/getting-started/#requirements) in *Getting Started*. - Ensure all native dependencies are properly synced with `npx cap sync`. - Check that your iOS and Android projects are correctly configured. ## Initialization errors If you encounter errors during SDK initialization: - Verify your credentials in the Airship dashboard. The credentials used by `takeOff` are your Airship project's [App Key](https://www.airship.com/docs/reference/glossary/#app_key) and [App Secret](https://www.airship.com/docs/reference/glossary/#app_secret). To find them, select the dropdown menu (▼) next to your project name, and then **Project details**. - Check that `takeOff` is called correctly in your app or configured in `capacitor.config.json`. - Ensure both iOS and Android native modules are properly installed. # Troubleshooting Push Notifications > Check push notification status and fix common issues. If [push notifications](https://www.airship.com/docs/developer/sdk-integration/capacitor/push-notifications/) aren't working as expected, you can check the notification status to diagnose the issue. ## Push Notifications Not Working If push notifications are not being received: - Verify that push notifications are enabled for both iOS and Android. - Check that APNs (iOS) and FCM (Android) are properly configured. - Ensure the app has notification permissions. ## Checking Push Notification Status If you're having trouble with push notifications, check the notification status to see the current state: ```typescript const status = await Airship.push.getNotificationStatus() console.log('Are notifications allowed:', status.areNotificationsAllowed) console.log('Is opted in:', status.isOptedIn) console.log('Is user notifications enabled:', status.isUserNotificationsEnabled) console.log('Is airship opted in:', status.isAirshipOptedIn) console.log('Is user opted in:', status.isUserOptedIn) console.log('Is pushable:', status.isPushable) console.log('Is privacy manager enabled:', status.isPrivacyManagerEnabled) ``` You can also listen for status changes to debug permission issues: ```typescript const handle = await Airship.push.onNotificationStatusChanged(event => { console.log('Notification status changed:', event.status) if (!event.status.areNotificationsAllowed) { console.log('System-level notifications are disabled') } if (!event.status.isUserNotificationsEnabled) { console.log('User notifications are disabled in Airship') } if (!event.status.isPushable) { console.log('Device is not pushable') } }) ```