> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pushnotif.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Delivery telemetry

> Add the Notification Service Extension so you see which pushes actually reached a device.

APNs only ever confirms that it **accepted** a push. Whether it reached the
phone is only knowable on the phone. Our Notification Service Extension runs
there when a push arrives and reports it, which completes the funnel:

```
sent -> accepted -> delivered -> opened / dismissed
```

Everything else works without it. You just never see `delivered`, and opened
counts can exceed delivered counts in the dashboard.

**1. Create the extension target**

In Xcode: **File > New > Target > Notification Service Extension**. Name it
anything, for example `NotificationService`. Replace the generated class
with a subclass of ours:

```swift theme={null}
import PushNotifAINSE

final class NotificationService: PushNotifAIServiceExtension {}
```

Link the `PushNotifAINSE` product to the extension target only.

**2. Create the app group**

In the Apple Developer portal, create an app group named:

```
group.<your-app-bundle-id>.pushnotifai
```

For the app `com.acme.app` that is `group.com.acme.app.pushnotifai`.

Add the **App Groups** capability with that group to **both** the app target
and the extension target, then regenerate your distribution provisioning
profiles, since they change when capabilities do.

**3. Ship it**

That's all. Following the naming convention means neither target needs any
configuration: each works out the group from its own bundle id.

> [!WARNING]
>
> **Without the app group, delivery reporting fails silently.** The extension
> can't find its configuration, passes the notification through untouched, and
> the delivered column stays empty. It looks exactly like a working integration
> until you notice the zeros.

## Using a different group name

If your group can't follow the convention, point both sides at it. In the
app:

```swift theme={null}
PushConfiguration(apiKey: "pna_...", baseURL: ..., appGroupId: "group.custom-id")
```

In the extension, add `PushNotifAIAppGroup` = `group.custom-id` to its
`Info.plist`, or override `appGroupId` in your subclass.

## How it behaves

* **Never delays your notification for long.** The report is attempted with a
  5 second timeout (override `beaconTimeout` to change it), then the
  notification is handed back to iOS either way.
* **Never loses a report.** It is written to disk in the app group before any
  network attempt. If the upload doesn't finish, the app sends it on next
  launch.
* **Never double counts.** Reports carry stable ids, so the extension and the
  app both sending one counts once.
* **Leaves other pushes alone.** A push that didn't come from PushNotifAI passes
  through untouched and isn't reported.

## Already have a Notification Service Extension?

Make your existing class subclass `PushNotifAIServiceExtension` instead of
`UNNotificationServiceExtension`, and call `super` from `didReceive` and
`serviceExtensionTimeWillExpire` for pushes you don't handle yourself.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.