# Push Notification Integration in Telegram-iOS: APNs and VoIP Push Implementation

> Explore Telegram-iOS push notification integration using APNs and VoIP push. Learn how registration, decryption, and VoIP payloads are managed for seamless communication. Discover the technical implementation.

- Repository: [TelegramMessenger/Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS)
- Tags: internals
- Published: 2026-04-07

---

**Telegram-iOS uses standard Apple Push Notification service (APNs) for encrypted messages and PushKit for VoIP calls, with registration handled in [`AppDelegate.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/AppDelegate.swift), decryption performed in a Notification Service Extension, and VoIP payloads routed through `pushRegistryImpl` to CallKit.**

Telegram-iOS implements a sophisticated dual-path push notification integration that leverages both standard APNs for secure messaging and PushKit for real-time voice and video calls. This architecture, found in the [TelegramMessenger/Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS) repository, cleanly separates concerns between the main app delegate for token registration, a notification service extension for payload decryption, and CallKit integration for incoming call handling.

## APNs Registration for Regular Push Notifications

During application launch in [`submodules/TelegramUI/Sources/AppDelegate.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/submodules/TelegramUI/Sources/AppDelegate.swift), the app initiates registration for standard remote notifications. Inside `application(_:didFinishLaunchingWithOptions:)`, the code checks for iOS 12.0 availability and requests an APNs token:

```swift
if #available(iOS 12.0, *) {
    UIApplication.shared.registerForRemoteNotifications()
}

```

*Source: lines ≈ 1597‑1599 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L1597-L1599)*.

When the system delivers the token via the delegate callback, Telegram stores it in a promise called `regularDeviceToken` for later use:

```swift
self.regularDeviceToken.set(.single(token))

```

*Source: lines ≈ 30‑31 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L30-L31)*.

This token is subsequently bundled into `NetworkInitializationArguments` as part of the `appData` payload sent to Telegram servers. The configuration uses `buildConfig.bundleData(... tokenType: "apns" ...)` to include the token in the registration request.

*Source: lines ≈ 71‑73 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L71-L73)*.

## VoIP Push Registration with PushKit

For incoming voice and video calls, Telegram integrates PushKit to receive high-priority VoIP pushes even when the app is terminated. The implementation creates a `PKPushRegistry` instance and configures it for VoIP notifications:

```swift
let pushRegistry = PKPushRegistry(queue: .main)
pushRegistry.desiredPushTypes = Set([.voIP])
pushRegistry.delegate = self
self.pushRegistry = pushRegistry

```

*Source: lines ≈ 1053‑1058 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L1053-L1058)*.

When the system provides VoIP credentials, the delegate stores the token in `voipTokenPromise`:

```swift
public func pushRegistry(_ registry: PKPushRegistry,
                         didUpdate credentials: PKPushCredentials,
                         for type: PKPushType) {
    if #available(iOS 9.0, *), case PKPushType.voIP = type {
        self.voipTokenPromise.set(.single(credentials.token))
    }
}

```

*Source: lines ≈ 2112‑2118 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L2112-L2118)*.

Similar to the APNs token, the VoIP token is incorporated into `NetworkInitializationArguments` and transmitted to Telegram's backend as part of the device registration process.

## Handling Incoming VoIP Pushes and CallKit Integration

The `PKPushRegistryDelegate` implementation funnels incoming pushes to a centralized helper method called `pushRegistryImpl`. When a VoIP push arrives, the delegate method invokes this implementation:

```swift
public func pushRegistry(_ registry: PKPushRegistry,
                         didReceiveIncomingPushWith payload: PKPushPayload,
                         for type: PKPushType,
                         completion: @escaping () -> Void) {
    self.pushRegistryImpl(registry, didReceiveIncomingPushWith: payload,
                          for: type, completion: completion)
}

```

*Source: lines ≈ 2122‑2126 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L2122-L2126)*.

The `pushRegistryImpl` method performs four critical operations:

1. **Payload extraction** – Locates either a plain `accountId` field or an encrypted `p` field in the push dictionary.
2. **Decryption** – Uses the per-account master notification key (`notificationEncryptionKeyId`) stored in the account manager state to decrypt the payload.
3. **Call identification** – Checks for `loc-key` values of `"CONF_CALL_REQUEST"` or `"CONF_VIDEOCALL_REQUEST"` to identify incoming calls.
4. **CallKit reporting** – For calls, it generates a stable UUID via `CallSessionManager.getStableIncomingUUID` and reports the call to the system using `CXProvider.reportNewIncomingVoIPPushPayload`, which presents the native incoming call UI. For non-call notifications, it writes the payload to a temporary file and triggers the Notification Service Extension.

*Source: lines ≈ 2134‑2150 in [AppDelegate.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/submodules/TelegramUI/Sources/AppDelegate.swift#L2134-L2150)*.

## Notification Service Extension for Message Decryption

Regular message notifications are processed by the **Notification Service Extension** located in [`Telegram/NotificationService/Sources/NotificationService.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/Telegram/NotificationService/Sources/NotificationService.swift). This extension runs when an encrypted APNs push arrives and performs the following:

1. **Retrieves the encrypted payload** from the `"p"` field.
2. **Loads the master notification key** from the shared container using `existingMasterNotificationsKey(postbox:)`.
3. **Decrypts the content** using `decryptedNotificationPayload(key:data:)`.
4. **Builds rich notification content** including avatars, custom emojis, and message previews via `NotificationContentContext`.

The decryption flow inside `NotificationServiceHandler.init` follows this pattern:

```swift
guard let payloadData = Data(base64Encoded: encryptedPayload) else { … }
let notificationsKey = existingMasterNotificationsKey(postbox: stateManager.postbox)
let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData)

```

*Source: lines ≈ 3085‑3110 in [NotificationService.swift](https://github.com/TelegramMessenger/Telegram-iOS/blob/master/Telegram/NotificationService/Sources/NotificationService.swift#L3085-L3110)*.

After building the `UNNotificationContent` object, the extension returns it through the completion handler, allowing iOS to display the decrypted message to the user.

## Summary

- **APNs registration** occurs in [`AppDelegate.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/AppDelegate.swift) via `UIApplication.shared.registerForRemoteNotifications()`, storing the token in `regularDeviceToken` for backend registration.
- **VoIP registration** uses `PKPushRegistry` with `.voIP` type, saving credentials to `voipTokenPromise`.
- **Push processing** routes VoIP payloads through `pushRegistryImpl`, which decrypts them and either reports to **CallKit** for calls or forwards to the **Notification Service Extension** for messages.
- **Decryption** happens in [`NotificationService.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/NotificationService.swift) using per-account master keys retrieved from the shared container, enabling rich notification content with media and avatars.

## Frequently Asked Questions

### How does Telegram-iOS decrypt push notification payloads?

The app encrypts notification payloads server-side using per-account master keys. When a push arrives, the **Notification Service Extension** in [`NotificationService.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/NotificationService.swift) retrieves the master key from the shared container using `existingMasterNotificationsKey(postbox:)`, then decrypts the payload via `decryptedNotificationPayload(key:data:)`. This allows the app to display message content and sender avatars without waking the main app process.

### What is the difference between APNs and VoIP push handling in Telegram-iOS?

**APNs pushes** are handled by the Notification Service Extension and are used for messages, media, and group notifications. **VoIP pushes** are handled directly by the main app via `PKPushRegistryDelegate` in [`AppDelegate.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/AppDelegate.swift), enabling immediate CallKit integration for incoming voice and video calls. VoIP pushes bypass the extension and are processed in `pushRegistryImpl`, which can wake the app from a terminated state.

### How does the app register push notification tokens with Telegram servers?

Both APNs and VoIP tokens are stored in promise objects (`regularDeviceToken` and `voipTokenPromise`) during delegate callbacks. These tokens are then incorporated into `NetworkInitializationArguments` as part of the `appData` payload using `buildConfig.bundleData(... tokenType: "apns")`, which is transmitted to Telegram's backend during the network initialization sequence.

### Where does CallKit integration occur for incoming calls?

CallKit integration happens inside `pushRegistryImpl` in [`AppDelegate.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/AppDelegate.swift) (around lines 2134‑2150). When the decrypted payload contains `loc-key` values indicating a call request (`"CONF_CALL_REQUEST"` or `"CONF_VIDEOCALL_REQUEST"`), the method generates a stable UUID and invokes `CXProvider.reportNewIncomingVoIPPushPayload` to present the system incoming call UI, ensuring the user can answer calls from the lock screen.