Інтеграція push-сповіщень через Apple Push Notification Service (APNs)
Push-сповіщення в iOS виглядають як несклана задача ровно до моменту, коли сповіщення доходять на симулятор, але не на реальне пристрій. Або приходять у дев-окружінні й пропадають у продакшене. Причина майже завжди одна: неправильна конфігурація сертифікатів або невідповідність окруження (sandbox vs production). APNs — строга система, й будь-яке невідповідність конфігурації приводить до мовчазної втрати сповіщень без помилки на клієнті.
Налаштування: p8 ключ vs p12 сертифікат
Apple підтримує два способи аутентифікації для APNs:
APNs Auth Key (.p8) — JWT-токен. Один ключ на весь акаунт, не істекає (тільки якщо не відозвати), працює для sandbox й production без переключення. Рекомендований спосіб з 2016 року.
APNs Certificate (.p12) — SSL-сертифікат. Істекає через рік, окремий для sandbox й production, прив'язаний до конкретного Bundle ID. Застарілий спосіб, але ще зустрічається в legacy проектах.
Для нового проекту завжди обираємо .p8 через Apple Developer Console → Certificates, Identifiers & Profiles → Keys.
Конфігурація на стороні iOS-додатку
// AppDelegate.swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in
guard granted else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
// Надсилаємо token на сервер
NotificationService.shared.registerToken(tokenString)
}
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("APNs registration failed: \(error)")
}
didFailToRegisterForRemoteNotificationsWithError — цей метод багато хто забуває реалізувати. Без нього мовчазний сбій реєстрації ніяк не логується.
Capabilities та Entitlements
В Xcode: Target → Signing & Capabilities → + Capability → Push Notifications. Це додає aps-environment в .entitlements. Значення — development для debug, production для release. Невідповідність цього значення при відправці сповіщення — найчастіша причина BadDeviceToken помилки від APNs.
Також додаємо Background Modes → Remote notifications, якщо потрібна фонова обробка.
Типи payload
Стандартний alert:
{
"aps": {
"alert": {
"title": "Нове сообщение",
"body": "Іван написав вам"
},
"badge": 3,
"sound": "default"
},
"userId": "u123",
"messageId": "m456"
}
Silent push (фонове оновлення без UI):
{
"aps": {
"content-available": 1
},
"syncType": "messages"
}
Silent push вимагає Background Modes → Remote notifications у Capabilities. На iOS 13+ Apple обмежує кількість silent push до ~3 на годину — не можна використовувати як заміну polling.
Notification Service Extension
Для модифікації сповіщень перед показом (розшифровка, завантаження зображення) — NotificationServiceExtension. Окремий таргет в Xcode, обробляє сповіщення з mutable-content: 1 у payload:
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
guard let bestAttempt = request.content.mutableCopy() as? UNMutableNotificationContent,
let attachmentURL = request.content.userInfo["imageUrl"] as? String,
let url = URL(string: attachmentURL) else {
contentHandler(request.content)
return
}
// Завантажуємо зображення й прикріпляємо
downloadAttachment(from: url) { attachment in
if let attachment { bestAttempt.attachments = [attachment] }
contentHandler(bestAttempt)
}
}
}
Таймаут Extension — 30 секунд. Якщо не встигли — APNs показує оригінальне сповіщення.
Токен та його життєвий цикл
Device token змінюється: при переустановці додатку, при відновленні з бекапу, іноді при оновленні iOS. Сервер повинен оновлювати токен при кожному didRegisterForRemoteNotificationsWithDeviceToken. APNs повертає 410 Gone при відправці на застарілий токен — сервер повинен його видаляти.
Відладка
-
Simulator: APNs працює тільки на фізичних пристроях (до Xcode 11.4 взагалі не працював, з 11.4+ — через
.apnsфайл у симуляторі) -
Console.app: фільтр по
dasdтаapsdпроцесам — там логи APNs daemon - Instruments → Push Notifications: трекінг delivery
Що входить у роботу
- Налаштування APNs Auth Key (.p8) в Apple Developer Console
- Capabilities та Entitlements в Xcode
- Реєстрація, отримання токена, обробка lifecycle токена
- Обробка foreground / background / terminated станів
- Notification Service Extension для rich notifications
- Silent push для фонової синхронізації
- Інтеграція з backend для зберігання та відправки токенів
Терміни
Базова інтеграція APNs з alert-сповіщеннями: 1 день. З rich notifications, silent push, Extension й повним lifecycle токена: 2–3 дні.







