Skip to main content
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:
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:
Link the PushNotifAINSE product to the extension target only.
2

Create the app group

In the Apple Developer portal, create an app group named:
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.
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:
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.