Enable push notifications in a Cordova app (iOS)

This guide walks you through enabling push notifications in your Cordova app on iOS using the Acoustic Connect SDK.

Language: JavaScript, with TypeScript definitions. The Notification Service Extension and Notification Content Extension are implemented in Swift and ship with the plugin — you don't write or maintain this Swift code yourself.

Availability: Pro, Premium, and Ultimate

Scope: Test setup — building, running on the simulator or a connected iPhone, and verifying push delivery. Covers automatic push mode.

Sample app: See our GitHub repository for a reference implementation to compare your configuration against.


Prerequisites

  • Apple Developer Program membership — push notifications require a paid account. Required role: Account Holder or Admin.
  • The Connect Cordova SDK, integrated in your project with a Connect app key from a Mobile app integration created after April 2026 — earlier integrations don't support push. See Integrate the Connect SDK into a Cordova app to install the plugin first.

Overview

  1. Get an APNs authentication key.
  2. Share your push notification credentials with your Connect administrator.
  3. Set up the Apple Developer portal.
  4. Update ConnectConfig.json in your project.
  5. Add entitlements and build for iOS.
  6. Add code to request notification permission from your users.

Step 1: Get an APNs authentication key

Connect uses token-based APNs authentication. A single .p8 key works across all apps in your team.

  1. In your Apple Developer account, go to Certificates, Identifiers & Profiles > Keys > +.
  2. Enter a name and enable Apple Push Notifications service (APNs).
  3. Click Configure, set Environment to Sandbox & Production unless your security team requires tighter scoping — this can't be changed later.
  4. Save > Continue > Register.
  5. Download the .p8 file — Apple only allows this once.
  6. Note the Key ID and your Team ID (10-character string, top-right of the portal).

Step 2: Share push notification credentials with your Connect administrator

CredentialWhere to find it
Encryption keyThe .p8 file from Step 1
Key IDThe confirmation page, or the file name
Team IDTop-right of the Apple Developer portal, or under Membership
Bundle IDYour Cordova project's config.xml — the id attribute on the <widget> element
EnvironmentSandbox — this guide covers test setup
📘

Note

Your Connect administrator needs to upload these credentials before push notifications will be delivered. See Connect mobile apps in the Connect user guide. Steps 3–5 can be completed while you wait.


Step 3: Set up the Apple Developer portal

Push notifications in Connect require three components sharing an App Group — a container that lets them exchange push data. The identifier must be identical across all three or delivery tracking and rich media silently fail.

ComponentPurpose
Main appInitializes the SDK and requests notification permission
Notification Service Extension (NSE)Downloads rich media and records delivery, including dismissed notifications
Notification Content Extension (NCE)Renders the expanded notification view on long-press

Do the following in the portal:

Register or update your App ID

If you are registering a new App ID:

  1. In your Apple Developer account, go to Identifiers > +.
  2. Select App IDs > App and click Continue.
  3. Enter a description and your bundle ID.
  4. Under Capabilities, enable Push Notifications and App Groups.
  5. Click Continue > Register.

If your App ID already exists:

  1. Click the App ID in the list to open it.
  2. Under Capabilities, enable Push Notifications and App Groups.
  3. Click Save.

Register App IDs for the extensions

This step is automated — you don't do it in the portal. The plugin creates the ConnectNSE and ConnectNCE extension targets in Xcode for you on every build (see Step 5).

Create an App Group

  1. Go to Identifiers > + and select App Groups.
  2. Enter a description and an identifier using the group. prefix (for example, group.com.yourcompany.yourapp).
  3. Click Continue > Register.
  4. Go back to your main App ID and assign the App Group you just created.

Xcode's automatic signing assigns this same App Group to the extension App IDs when you build in Step 5.


Step 4: Update ConnectConfig.json

Back in your project root (outside Xcode), open ConnectConfig.json and add the following keys inside the Connect object.

{
  "Connect": {
    "AppKey": "...",
    "PostMessageUrl": "...",
    "iOSPushMode": "automatic",
    "iOSAppGroupIdentifier": "group.com.yourcompany.yourapp",
    "iOSDevelopmentTeam": "YOUR_TEAM_ID"
  }
}
KeyValue
iOSPushMode'automatic' (default) — the SDK handles APNs registration, delivery, and tap actions.
iOSAppGroupIdentifierThe App Group identifier from Step 3. Substituted automatically into the NSE/NCE Swift source and into the host app's and both extensions' entitlements files — you don't edit any .entitlements file by hand.
iOSDevelopmentTeamRequired. Your 10-character Apple Team ID. Stamped onto the main app target and both extension targets automatically on every prepare.

Step 5: Add entitlements and build

Before your first build, add an entitlements file and a hook so the Push Notifications capability is applied automatically on every prepare — no manual Xcode step needed.

  1. Create the entitlements/ folder at your Cordova project root and add a file named App.entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>development</string>
</dict>
</plist>
  1. Create hooks/after_prepare_entitlements.js:
#!/usr/bin/env node
'use strict';

const fs = require('fs');
const path = require('path');

module.exports = function (context) {
    const platforms = context.opts.platforms || [];
    if (!platforms.includes('ios')) return;

    const projectRoot = context.opts.projectRoot;
    const platformRoot = path.join(projectRoot, 'platforms', 'ios');
    if (!fs.existsSync(platformRoot)) return;

    const xcodeproj = fs.readdirSync(platformRoot).find(f => f.endsWith('.xcodeproj'));
    if (!xcodeproj) return;
    const appName = xcodeproj.replace('.xcodeproj', '');
    const appDir = path.join(platformRoot, appName);

    const src = path.join(projectRoot, 'entitlements', 'App.entitlements');
    if (!fs.existsSync(src)) return;
    fs.mkdirSync(appDir, { recursive: true });
    fs.copyFileSync(src, path.join(appDir, 'App.entitlements'));

    const setting = `CODE_SIGN_ENTITLEMENTS = $(PROJECT_DIR)/${appName}/App.entitlements`;
    ['build-debug.xcconfig', 'build-release.xcconfig'].forEach(cfg => {
        const cfgPath = path.join(platformRoot, 'cordova', cfg);
        if (!fs.existsSync(cfgPath)) return;
        const content = fs.readFileSync(cfgPath, 'utf8');
        if (!content.includes('CODE_SIGN_ENTITLEMENTS')) {
            fs.appendFileSync(cfgPath, `\n${setting}\n`);
        }
    });
};
  1. Register the hook in config.xml under the iOS platform block:
<platform name="ios">
    <hook type="after_prepare" src="hooks/after_prepare_entitlements.js" />
</platform>
  1. Build. Run from your Cordova project root — the directory containing config.xml and ConnectConfig.json.
npx cordova build ios

Every build wires up Xcode for you automatically — you do not need to manually create extension targets, edit a Podfile, or open Xcode to add capabilities.

What happens automatically on every build
  • Your entitlements hook copies App.entitlements into the Xcode project and sets CODE_SIGN_ENTITLEMENTS in the build configuration, applying the Push Notifications capability without a manual Xcode step.
  • The plugin copies the NSE (NotificationService.swift) and NCE (NotificationViewController.swift) source into platforms/ios/, with your App Group identifier substituted in.
  • Creates the ConnectNSE and ConnectNCE app-extension targets in your .xcodeproj (idempotently — safe to run every prepare), links the required system frameworks (UserNotifications, and UserNotificationsUI + UIKit for the NCE), and adds an "Embed Foundation Extensions" build phase on the main app target.
  • Adds ConnectNSE and ConnectNCE as CocoaPods targets in your Podfile and runs pod install — only when the Podfile actually changed, to avoid an unnecessary reinstall on every build.
  • Adds the App Group and aps-environment entitlement to the main app's entitlements file (and both extensions').
  • Adds UIBackgroundModesremote-notification to Info.plist.
  • Sets DEVELOPMENT_TEAM and CODE_SIGN_STYLE = Automatic on all three targets (main app, ConnectNSE, ConnectNCE) using the iOSDevelopmentTeam value from ConnectConfig.json, so Xcode manages provisioning profiles automatically.

After the build completes, open platforms/ios/App.xcworkspace to run the app. Xcode's automatic signing registers the extension App IDs and assigns the App Group in the Apple Developer portal — no manual portal configuration is needed for the extensions.


Step 6: Request notification permission

const result = await AcousticConnect.push.requestPermission();

if (result.granted) {
  console.log('Notification permission granted');
} else {
  console.log('Notification permission denied:', result.error);
}

push.requestPermission() presents the system permission dialog when the state is undetermined. Call it at an appropriate moment in your UX rather than on first launch.

🚧

Warning

iOS only shows the system permission dialog once. If the user denies it, the only way to re-enable notifications is through the Settings app.

Check the current state without prompting:

const granted = await AcousticConnect.push.getPermissionState();
// true = granted, false = denied, null = not yet determined

Message structure

After you complete the setup, your marketing team creates message content in Message composer in Connect. The following elements make up a push message, and each has its own source and constraints.

  • Notification icon — shown alongside every notification. iOS uses your app icon (Assets.xcassets/AppIcon.appiconset) for all messages. iOS does not support custom notification icons.

  • Title and body — authored per message. Title: up to 50 characters. Body: up to 150 characters.

  • Action — what happens when the user taps the notification: open the app, call a number, or open a URL.

  • Thumbnail image (optional) — shown to the right of the notification body. Accepted formats: .jpg, .jpeg, .png. Minimum dimensions: 64 × 64 px. Maximum size: 5 MB.

  • Expandable content — optionally add expanded text or an expanded image, shown when the user long-presses the notification. Accepted image formats: .jpg, .jpeg, .gif, .png. Minimum dimensions: 1024 × 1024 px. Maximum size: 5 MB. The image is downloaded by the Notification Service Extension and requires mutable-content: 1 in the APNs payload.


Testing

On the simulator — quick test

You can test push delivery in the simulator using .apns payload files. Create them in any text editor and drag them onto the running simulator to send a test notification. We keep ours in a TestPayloads/ folder inside the project. This is not a full verification: the simulator confirms basic delivery and NCE invocation, but expanded images in rich notifications may not have time to load. Use a real iPhone to validate that images load correctly.

{
    "Simulator Target Bundle": "com.yourcompany.yourapp",
    "aps": {
        "alert": { "title": "New arrivals", "body": "Fresh styles just landed." },
        "sound": "default"
    }
}
{
    "Simulator Target Bundle": "com.yourcompany.yourapp",
    "aps": {
        "alert": { "title": "New arrivals", "body": "Fresh styles just landed." },
        "sound": "default",
        "mutable-content": 1,
        "category": "ACOUSTIC_RICH_NOTIFICATION"
    },
    "expandedImage": "https://example.com/image.jpg"
}

On an iPhone — server-sent delivery

  1. Build and run on a physical iPhone.
  2. Grant notification permission when prompted. A new mobile contact is created in Connect.
  3. Coordinate with your marketing team to send a test push, or do it yourself — see Test your push notification setup.
  4. Verify the notification appears, images load, and push signals appear in the contact's activity feed in Connect.

Verification

The steps above are enough to confirm push is working. This section is optional — use it to inspect what the SDK is doing under the hood, or to collect diagnostic information when something isn't working as expected.

Enable debug logging

Confirm "useRelease": false in ConnectConfig.json (the default for development). Then add these environment variables with a value of 1 to your scheme so the Xcode console shows verbose SDK output for push registration and delivery: CONNECT_DEBUG, TLF_DEBUG, and EODebug.

If you ever need to report an issue to support, attach your debug log.

Verify push registration in the debug log

After you accept the notification permission request, check the Xcode console for these log entries, in order:

EntryWhat it means
Flushing <Connect.ConnectPushRegistrationMessage: ...>Push registration message queued for delivery
Connected to wifi, using WiFi log levelNetwork check passed
url For flush: https://...collectorPostRegistration data posted to the collector

Then filter by pushRegistration to find the JSON payload. Look for these fields:

FieldExpected valueWhat it means
acceptPushtrueNotification permission granted
mobileProviderAPNRegistered with APNs
mobileTokenpresent (long hex string)Device token obtained — the SDK can receive push. On the simulator, a token is only generated on iOS 16 or later.
newRegistrationtrue on first run, false afterWhether this is a new registration or a refresh

If acceptPush is missing, notification permission has not been granted — check that push.requestPermission() is being called. If mobileToken is absent, confirm the Push Notifications capability is added and the device has network access.

Log entries that are safe to ignore
EntryReason
APNs device token registration failed: ... remote notifications are not supported in the simulatorExpected on iOS 15 simulators — APNs tokens are only generated on iOS 16 or later
THREAD WARNING: ['ConnectPlugin']Performance note, not a failure
EOCoreSettings bundle not foundExpected with programmatic config — no settings bundle needed
Value for key URLEncoded could not be foundConfig lookup miss — default is used
Found 0 saved queuesNormal on first launch

Troubleshooting

Push notifications not being delivered

  • Check your debug log for an APNs token. If none appears, confirm the Push Notifications capability is added to the main app target and that the device has network access. On the simulator, APNs tokens are only generated on iOS 16 or later.
  • Confirm the user granted notification permission.
  • Confirm remote-notification is in UIBackgroundModes in Info.plist (added automatically — check if you've overridden Info.plist manually).
  • Confirm the bundle ID matches the App ID with Push Notifications enabled in the portal.

Rich push images not showing

  • Confirm the image URL is accessible over HTTPS.
  • Confirm iOSAppGroupIdentifier in ConnectConfig.json matches the App Group on your main App ID in the portal. Xcode's automatic signing applies it to the extension App IDs.
  • Confirm pod install ran after any App Group change — check the after_prepare console output for Podfile changed — running pod install.

Extension targets missing or stale after a fresh clone / plugin re-add

Run npx cordova prepare ios (or npx cordova build ios) again — the extension targets, embed phase, and entitlements are rebuilt idempotently by the after_prepare hook each time. If signing looks stale specifically, re-open Xcode, confirm Signing & Capabilities shows a Team on all three targets, and re-download provisioning profiles if App Groups isn't selectable.

[after_prepare] pod install failed

CocoaPods isn't installed, or your local spec repo is stale. Run sudo gem install cocoapods && pod repo update, then npx cordova build ios again.

Unable to find host target(s) for ConnectNSE, ConnectNCE during pod install

This can happen on a fresh platform add, when the Podfile still references extension targets from a previous prepare but the .xcodeproj was reset. The plugin's before_prepare hook detects and strips stale references automatically, and after_prepare re-adds them once the project is consistent again — if you still see this error, re-run npx cordova prepare ios once more before investigating further.


Next step: Identify app users

Push setup is now complete. If you haven't already, identify the people receiving your push notifications — see Identify users at sign-in.


Did this page help you?