Реалізація Notification Service Extension для iOS
Notification Service Extension — окремий target у Xcode, який перехоплює вхідне push-сповіщення до його показу користувачу. У цьому вікні (не більше 30 секунд) можна змінити заголовок, тіло, додати media attachment або розшифрувати payload. Якщо Extension не встигає або падає — показується оригінальне сповіщення.
Для чого використовується на практиці
Media attachments. Додаємо зображення до push-сповіщення. Сервер відправляє URL у payload (mutable-content: 1 обов'язковий). Extension завантажує зображення через URLSession, зберігає у тимчасову директорію, створює UNNotificationAttachment та передає через contentHandler. Без Extension attachments у push не працюють.
Сквозне шифрування. Payload приходить зашифрованим, Extension розшифровує з ключем з Keychain та підставляє читаний текст. Без цього push-сповіщення у e2e-мессенджерах показували б «Нове повідомлення» без змісту.
Аналітика доставки. У Extension робимо fire-and-forget запит на backend при отриманні сповіщення — фіксуємо delivered событие. UIApplicationDelegate.userNotificationCenter(_:didReceive:) спрацьовує лише при тапі, Extension — при доставці.
Технічні деталі
Extension живе у окремому process, не має прямого доступу до даних основного додатку. Для shарингу даних (наприклад, ключи шифрування) використовуємо App Groups: UserDefaults(suiteName: "group.com.example.app") та FileManager з контейнером group'и.
Keychain Sharing: kSecAttrAccessGroup з group identifier'ом дозволяє Extension читати secrets, записані основним додатком.
Таймаут 30 секунд — жорсткий. Якщо завантажуємо зображення, потрібен таймаут на URLSession менше 30 секунд з fallback: якщо не встигли — показуємо сповіщення без attachment, не фейлимо Extension. URLSessionConfiguration.default.timeoutIntervalForRequest = 20.
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// завантажуємо attachment, при помилці — contentHandler(bestAttemptContent!)
}
override func serviceExtensionTimeWillExpire() {
// викликається за мгновення до таймаута — показуємо що є
if let contentHandler, let content = bestAttemptContent {
contentHandler(content)
}
}
Що входить у роботу
- Створення та налаштування target Notification Service Extension у Xcode
- Реалізація логіки: attachment, розшифровка або аналітика
- Налаштування App Groups для shарингу даних з основним додатком
- Тестування на реальному пристрої (Extension не працює в симуляторі для push від APNs)
- Entitlements та provisioning profiles для target Extension'а
Терміни
Реалізація одного сценарію (наприклад, attachment): 1 день. З шифруванням, App Groups та кількома логіками обробки: 2–3 дні. Вартість рассчитується індивідуально.







