Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions contents/docs/libraries/android/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,48 @@ config.addBeforeSend { event ->
}
```

# Push notifications

The Android SDK can register a device for [Workflows](/docs/workflows) push notifications and capture when a user opens one. Sends go out through the FCM channel you connect in [Workflows > Channels](/docs/workflows/configure-channels?tab=Push), so the Firebase project on that channel must match your app.

## Automatic registration and open tracking (default)

Both behaviors are on by default. When `firebase-messaging` is on your classpath, the SDK registers this device's FCM token with PostHog, and it auto-captures a `$push_notification_opened` event when a user opens the app from a notification tray tap.

```kotlin
val config = PostHogAndroidConfig(apiKey = "<ph_project_api_key>").apply {
capturePushNotificationSubscriptions = true // register the FCM token with PostHog
capturePushNotificationOpened = true // capture `$push_notification_opened`
}
PostHogAndroid.setup(context, config)
```

Firebase delivers rotated tokens through `onNewToken`, which the SDK can't observe on its own — forward it so the registered token stays current:

```kotlin
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
PostHog.registerPushNotificationToken(token, appId = "your-firebase-project-id")
}
}
```

## Manual registration

```kotlin
// Register a device token (appId = your Firebase project id):
PostHog.registerPushNotificationToken(token, appId = "your-firebase-project-id")

// Unregister when a user signs out:
PostHog.unregisterPushNotificationToken()
```

Calling `PostHog.reset()` on logout unregisters the token for the signed-out user and re-registers it under the new anonymous id.

## Identity verification

Identity verification (a signed token that proves *who* a device belongs to when registering) is **not yet available in the Android SDK** — it currently ships only in iOS. If your push channel is set to **Required**, Android device registration will be rejected until Android support lands, so keep the channel on **Disabled** or **Optional** for Android users in the meantime.

# FAQ

## What Android API level is required?
Expand Down
110 changes: 110 additions & 0 deletions contents/docs/libraries/ios/push-notifications.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: iOS push notifications
sidebarTitle: Push notifications
sidebar: Docs
showTitle: true
github: 'https://github.com/PostHog/posthog-ios'
platformLogo: ios
---

The iOS SDK can register a device for [Workflows](/docs/workflows) push notifications and capture when a user opens one. Sends go out through the FCM or APNs channel you connect in [Workflows > Channels](/docs/workflows/configure-channels?tab=Push).

<CalloutBox icon="IconInfo" title="What the SDK does and doesn't do" type="fyi">

The SDK registers the device token with PostHog and captures opens. You still set up remote notifications and request notification permission in your app as usual — the SDK doesn't do that for you.

</CalloutBox>

## Requirements

- Enable the **Push Notifications** capability for your app and register for remote notifications (`registerForRemoteNotifications()`), which requires requesting notification permission from the user.
- A connected **FCM** or **APNs** channel in PostHog whose Firebase project / APNs topic (bundle id) matches your app.

## Automatic registration and open tracking (default)

By default the SDK registers the device token and captures opens for you, by hooking into the system callbacks:

```swift
let config = PostHogConfig(apiKey: "<ph_project_api_key>")
// Both default to true — shown here for clarity:
config.capturePushNotificationSubscriptions = true // register the device token with PostHog
config.capturePushNotificationOpened = true // capture `$push_notification_opened` on tap
PostHogSDK.shared.setup(config)
```

With these enabled you only need to register for remote notifications; the SDK picks up the token and open events automatically. Locally-scheduled notifications are ignored — capture those manually (below).

## Manual registration

If you manage the notification lifecycle yourself, turn the automatic flags off and call the SDK directly.

Register the device token from `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`:

```swift
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
PostHogSDK.shared.registerPushNotificationToken(token)
// Or, to route to a specific channel by app id (Firebase project id / APNs bundle id):
// PostHogSDK.shared.registerPushNotificationToken(token, appId: "com.example.app")
}
```

Unregister the token when a user signs out so it isn't left bound to them:

```swift
PostHogSDK.shared.unregisterPushNotificationToken()
```

Calling `PostHogSDK.shared.reset()` on logout also unregisters the token for the signed-out user.

## Capturing opens

When automatic capture is on, tapping a remote push emits a `$push_notification_opened` event. To capture opens yourself (or to capture a locally-scheduled notification), call:

```swift
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
PostHogSDK.shared.capturePushNotificationOpened(response: response)
completionHandler()
}
```

There's also a fully manual variant if you don't have a `UNNotificationResponse`:

```swift
PostHogSDK.shared.capturePushNotificationOpened(title: "...", body: "...", payload: [:], action: "...")
```

## Identity verification

<CalloutBox icon="IconWarning" title="Requires an in-progress SDK release" type="caution">

The `pushIdentityProvider` hook ships in [posthog-ios #735](https://github.com/PostHog/posthog-ios/pull/735) — confirm your SDK version includes it before relying on this. Identity verification is not yet available in the Android SDK.

</CalloutBox>

A device token says *where* to deliver a notification, not *who* the device belongs to. If you turn on identity verification for your push channel ([Optional or Required](/docs/workflows/configure-channels?tab=Push)), your app must attach a short-lived token that proves the logged-in user's identity when it registers a device.

Your **backend** mints that token — the SDK never signs one itself. Sign an HS256 JWT with your project's secret API key, with the user's `distinct_id` as `sub`, `aud` set to `posthog:push_identity`, and a short `exp`. Then supply it through `pushIdentityProvider`:

```swift
config.pushIdentityProvider = { distinctId, appId, completion in
// Fetch a freshly-minted token from your backend for this user, then:
completion(token) // or completion(nil) to send without one
}
```

Notes:

- The SDK calls your provider when it needs a token and caches the result in memory per `(distinctId, appId)`, re-requesting on identity change and when a request is rejected. Always call `completion` exactly once.
- If your provider doesn't respond in time, the SDK falls back to sending without a token. On a channel set to **Required** that request is rejected, so make sure your provider reliably returns a token before switching the channel to Required.

## Troubleshooting

| Issue | Check |
| --- | --- |
| Token never registers | Confirm you call `registerForRemoteNotifications()` and the user granted notification permission. |
| Push doesn't arrive | Confirm the channel's APNs environment (Production/Sandbox) matches your build, and the bundle id matches. |
| Registration rejected on a Required channel | Your `pushIdentityProvider` isn't returning a valid token in time — see [Identity verification](#identity-verification). |
4 changes: 4 additions & 0 deletions src/navs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2540,6 +2540,10 @@ export const docsMenu = {
name: 'Configuration',
url: '/docs/libraries/ios/configuration',
},
{
name: 'Push notifications',
url: '/docs/libraries/ios/push-notifications',
},
],
},
{
Expand Down
Loading