# Notification Service Extension

{% hint style="warning" %}
**Required for Delivery Tracking:** The Notification Service Extension is required for accurate delivery tracking. Setting up an App Group without implementing this extension will result in inaccurate delivery metrics.
{% endhint %}

## Overview

ContextPush requires a Notification Service Extension to track notification delivery when your app is not running. The extension allows iOS to wake a small piece of your code when a notification arrives, enabling delivery confirmation even when the main app is terminated.

**Benefits:**

* Accurate delivery tracking and metrics
* Context capture at notification delivery time
* Better analytics for notification effectiveness

## How Delivery Tracking Works

When you configure an `appGroupIdentifier` in your ContextSDK configuration, you are enabling delivery tracking. This tells ContextSDK that:

1. Your app has a Notification Service Extension set up
2. The extension will report traditional notification deliveries to ContextPush servers

{% hint style="danger" %}
**Important:** If you set an `appGroupIdentifier` but do not implement the Notification Service Extension, your traditional delivery tracking data will be inaccurate, or messages maybe delivered multiple times.
{% endhint %}

## Prerequisites

Before setting up the extension, ensure you have:

* Completed the basic [ContextPush integration](/context-push/integration.md)
* Xcode 14.0 or later
* iOS 14.0 deployment target or higher

***

## Step 1: Create App Group

App Groups enable data sharing between your main app and the extension. Both need access to the same license verification token and shared data.

{% hint style="info" %}
**App Group = Delivery Tracking Enabled:** When you configure an App Group in your main app's Configuration, ContextSDK enables delivery tracking. This is why completing **all steps** in this guide is essential—skipping the extension implementation will cause inaccurate metrics.
{% endhint %}

{% stepper %}
{% step %}

#### Create an App Group Identifier

1. Go to the [Apple Developer Portal](https://developer.apple.com/account/resources/identifiers/list/applicationGroup)
2. Click the **+** button to create a new identifier
3. Select **App Groups** and click **Continue**
4. Enter a description and identifier (e.g., `group.com.yourcompany.yourapp`)
5. Click **Register**
   {% endstep %}

{% step %}

#### Enable App Groups in Your Main App

1. Open your Xcode project
2. Select your **main app target**
3. Go to **Signing & Capabilities**
4. Click **+ Capability** and add **App Groups**
5. Check the app group you created (or click **+** to add it)
   {% endstep %}
   {% endstepper %}

***

## Step 2: Create Notification Service Extension

{% stepper %}
{% step %}

#### Add Extension Target

1. In Xcode, go to **File** → **New** → **Target**
2. Select **Notification Service Extension**
3. Enter a name (e.g., `NotificationServiceExtension`)
4. Click **Finish**
5. If prompted to activate the scheme, click **Cancel** (you'll run the main app, not the extension directly)
   {% endstep %}

{% step %}

#### Enable App Groups in Extension

1. Select your **extension target**
2. Go to **Signing & Capabilities**
3. Click **+ Capability** and add **App Groups**
4. Check the **same app group** you added to the main app

{% hint style="warning" %}
Both the main app and extension must use the exact same App Group identifier.
{% endhint %}
{% endstep %}

{% step %}

#### Add ContextSDKExtension Dependency

{% tabs %}
{% tab title="CocoaPods" %}
Add the extension pod to your `Podfile`:

{% code title="Podfile" %}

```ruby
target 'YourApp' do
  pod 'ContextSDK'
end

target 'NotificationServiceExtension' do
  pod 'ContextSDKExtension'
end
```

{% endcode %}

Then run:

```bash
pod install
```

{% endtab %}

{% tab title="Swift Package Manager" %}

1. In Xcode, select your project in the navigator
2. Select the **extension target**
3. Go to **General** → **Frameworks, Libraries, and Embedded Content**
4. Click **+** and add `ContextSDKExtension` from the ContextSDK package
   {% endtab %}
   {% endtabs %}
   {% endstep %}
   {% endstepper %}

***

## Step 3: Configure Main App

Update your main app initialization to enable App Group support.

{% hint style="warning" %}
**Complete All Steps:** Adding the `appGroupIdentifier` to your Configuration enables delivery tracking. You **must** also complete Step 4 (Implement Extension) for accurate metrics. ContextSDK will log a warning if it detects an App Group configuration without a Notification Service Extension.
{% endhint %}

{% tabs %}
{% tab title="UIKit" %}
{% code title="AppDelegate.swift" overflow="wrap" %}

```swift
import ContextSDK

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // Configure ContextSDK with App Group support
    let config = Configuration(appGroupIdentifier: "group.com.yourcompany.yourapp")

    ContextManager.applicationDidFinishLaunchingWithOptions(
        launchOptions,
        licenseKey: "YOUR_LICENSE_KEY",
        configuration: config
    )

    // Continue with ContextPush setup...
    ContextPush.applicationDidFinishLaunchingWithOptions()

    return true
}
```

{% endcode %}
{% endtab %}

{% tab title="SwiftUI" %}
{% code title="AppDelegate.swift" overflow="wrap" %}

```swift
import ContextSDK

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Configure ContextSDK with App Group support
        let config = Configuration(appGroupIdentifier: "group.com.yourcompany.yourapp")

        ContextManager.applicationDidFinishLaunchingWithOptions(
            launchOptions,
            licenseKey: "YOUR_LICENSE_KEY",
            configuration: config
        )

        // Continue with ContextPush setup...
        ContextPush.applicationDidFinishLaunchingWithOptions()

        return true
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
The `appGroupIdentifier` must match exactly what you configured in both targets' capabilities.
{% endhint %}

***

## Step 4: Implement Extension

Replace the contents of your `NotificationService.swift` with:

{% code title="NotificationService.swift" overflow="wrap" %}

```swift
import UserNotifications
import ContextSDKExtension

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        guard let bestAttemptContent = bestAttemptContent else {
            contentHandler(request.content)
            return
        }

        // Initialize ContextSDK for the extension
        let success = ContextManager.setupForExtension(
            appGroupIdentifier: "group.com.yourcompany.yourapp",
            licenseKey: "YOUR_LICENSE_KEY"
        )

        guard success else {
            // License verification failed - deliver notification without tracking
            contentHandler(bestAttemptContent)
            return
        }

            // Report notification and capture context
        // This call waits for context capture and server reporting before completing
        ContextPush.handleNotification(request.content.userInfo) {
            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // iOS is about to terminate the extension - deliver what we have
        if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}
```

{% endcode %}

### How It Works

1. **`setupForExtension`** - Initializes the SDK using shared credentials from the App Group. Returns `false` if the main app hasn't been launched yet or license verification fails.
2. **`handleNotification`** - Reports the notification to ContextPush servers for accurate reporting.

{% hint style="warning" %}
**Important:** The extension has limited execution time (\~30 seconds). `handleNotification` is designed to complete quickly, but always implement `serviceExtensionTimeWillExpire()` as a fallback.
{% endhint %}

***

## Troubleshooting

### Extension Not Running

* Verify App Groups are enabled in both targets
* Check that both targets use the same App Group identifier

### License Verification Fails

* Launch the main app at least once after adding App Group support
* Verify the license key is identical in both main app and extension
* Check the App Group identifier matches exactly

***

## Next Steps

* Review [Analytics & Reporting](/context-push/analytics-and-reporting.md) to understand your reporting metrics
* Check the [Release Checklist](/context-push/release-checklist.md) before going to production


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.contextsdk.com/context-push/notification-service-extension.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
