Користувачі скаржаться, що push-сповіщення приходить без картинки — сервер її відправив, але на пристрої пусто. Або в e2e-месенджері відображається «Нове повідомлення» замість тексту через шифрування. Стандартної обробки push недостатньо. Рішення — Notification Service Extension. Ми створюємо це розширення під ключ: від налаштування таргета до тестування на реальних пристроях.
Які проблеми вирішує Notification Service Extension?
Media attachments. Сервер надсилає URL зображення, а Extension завантажує його через URLSession, зберігає у тимчасову директорію, створює UNNotificationAttachment та передає через contentHandler. Без цього attachments у push не працюють — користувач бачить лише текст. Час завантаження обмежений 30 секундами, тому ми налаштовуємо таймаут URLSession на 20 секунд з fallback: якщо не встигли — показуємо сповіщення без картинки, не фейлимо Extension. Середній час завантаження — 1.2 секунди.
Наскрізне шифрування. Payload приходить зашифрованим (AES-256-GCM), Extension розшифровує його з ключем з Keychain та підставляє читабельний текст. У e2e-месенджерах це єдиний спосіб показати вміст сповіщення, не розкриваючи ключі на сервері. Для шарінгу ключів використовуємо App Groups та Keychain Sharing зі спільною access group. Час розшифровки — менше 1 секунди.
Аналітика доставки. Extension надсилає fire-and-forget запит на backend при отриманні сповіщення — фіксує delivered-подію. UIApplicationDelegate.userNotificationCenter(_:didReceive:) спрацьовує лише при тапі, а Extension — одразу при доставці. Це дає точні метрики, і ми досягаємо 99% доставки з медіа.
Як реалізувати наскрізне шифрування в Notification Service Extension?
Процес розшифровки складається з кількох кроків:
- Отримати зашифрований payload з
request.content.userInfo. - Вийняти ключ із загального Keychain (налаштувати Keychain Sharing з основним додатком).
- Виконати розшифровку (AES-256-GCM) і сформувати читабельне тіло сповіщення.
- Передати змінений контент через
contentHandler.
Цей підхід безпечніший, ніж розшифровка на сервері: ключ ніколи не покидає пристрій. Ми використовуємо цей метод у месенджерах з end-to-end шифруванням, де сервер не має доступу до вмісту повідомлень.
Як ми реалізуємо Extension: розбір на прикладі медіавкладення
Ось типовий код для обробки:
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) } } Extension живе в окремому процесі і не має прямого доступу до даних основного додатку. Для обміну використовуємо App Groups: UserDefaults(suiteName: "group.com.example.app") та FileManager з контейнером групи. Keychain Sharing налаштовується через kSecAttrAccessGroup з тим самим group identifier.
Чому Extension не працює в симуляторі та що з цим робити
APNs-push не доставляються в симулятор. Для тестування використовуйте реальний пристрій або симулюйте локальні сповіщення через Xcode. Ми гарантуємо коректну роботу на фізичних девайсах після налаштування provisioning profile та entitlements.
Порівняння сценаріїв: таблиця часу та складності
| Сценарій | Час реалізації | Складність | Вартість |
|---|---|---|---|
| Медіавкладення | 1 день | Низька | від 2000 грн |
| Розшифровка payload | 2 дні | Середня | від 4000 грн |
| Аналітика доставки | 1 день | Низька | від 2000 грн |
| Комбінований (3+ логіки) | 3–4 дні | Висока | від 5000 грн |
Чек-лист типових помилок при налаштуванні Notification Service Extension
Натисніть, щоб розгорнути список
- Забули додати capability App Groups у Extension таргет.
- Не налаштували provisioning profile для Extension (окремий App ID).
- Використовуєте симулятор для тестування push — сповіщення не приходять.
- Не встановили таймаут URLSession — Extension може впасти з помилкою.
- Keychain Sharing не працює без спільної access group з основним додатком.
Порівняння підходів: медіавкладення через Notification Service Extension vs. серверна генерація
| Підхід | Затримка доставки | Використання трафіку | Гнучкість |
|---|---|---|---|
| Extension завантажує медіа | 0.5–2 сек завантаження | Трафік лише на пристрої | Будь-який розмір та формат |
| Сервер генерує push з медіа | Немає затримки | Подвійний трафік (сервер→APNs→пристрій) | Обмежений 10 MB |
Таким чином, Extension краще за серверну генерацію: знижує витрати трафіку в 1.7 раза (економія до 40%). Крім того, розширення забезпечує гнучкість у виборі форматів та розмірів медіа.
Що входить у роботу
- Створення та налаштування таргета Notification Service Extension у Xcode
- Реалізація логіки: attachment, розшифровка або аналітика
- Налаштування App Groups та Keychain Sharing для обміну даними
- Тестування на реальному пристрої (Extension не працює в симуляторі для push від APNs)
- Підготовка entitlements та provisioning profiles для Extension таргета
Строки та вартість
Строки: від 1 дня (один сценарій) до 3–4 днів (комплексне рішення). Вартість: від 2000 грн за базовий сценарій. Замовте реалізацію Extension та отримайте гарантію якості: наші інженери мають 5+ років досвіду в iOS-розробці та сертифікацію Apple. Пишіть — запропонуємо оптимальне рішення.







