Ви отримали задачу: кастомний інтерфейс для push-сповіщення iOS з картою доставки і кнопками «Прийняти» / «Відхилити». Стандартний UI iOS не підходить — потрібно своє розширення. Notification Content Extension — єдиний спосіб додати інтерактивний попередній перегляд без відкриття додатку. Часто стандартного preview недостатньо: потрібно відобразити карту, форму замовлення або кастомну кнопку. Для цього iOS надає механізм Notification Content Extension. Однак його налаштування вимагає акуратної роботи з UNNotificationCategory, правильної конфігурації Info.plist та розуміння sandbox-обмежень. За роки роботи ми накопичили 12+ успішних кейсів — від логістичних карт до інтерактивних опитувань.
Як працює Notification Content Extension для iOS?
Content Extension — це окремий таргет з власним UIViewController, що реалізує UNNotificationContentExtension. Коли користувач розгортає сповіщення з відповідною категорією, система завантажує ваш контролер і викликає didReceive(_:). Кнопки дій додаються через UNNotificationCategory в основному додатку. Payload повинен містити category з тим же ідентифікатором.
class NotificationViewController: UIViewController, UNNotificationContentExtension { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var bodyLabel: UILabel! func didReceive(_ notification: UNNotification) { let content = notification.request.content titleLabel.text = content.title bodyLabel.text = content.body // парсим content.userInfo для додаткових даних } func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { // обробка натискання на action buttons completion(.dismissAndForwardAction) } } Ключовий нюанс: реєстрація категорій — реалізація notification content
Категорію потрібно зареєструвати до відправки першого сповіщення — зазвичай в AppDelegate або UNUserNotificationCenter.current().setNotificationCategories([...]). Ідентифікатор категорії повинен збігатися з UNNotificationExtensionCategory в Info.plist розширення.
Як налаштувати кастомний розмір UI?
Висота задається в Info.plist через UNNotificationExtensionInitialContentSizeRatio (відношення висоти до ширини). Наприклад, 0.6 для картки замовлення. Якщо потрібна динамічна висота, змінюємо preferredContentSize в коді. Ми використовуємо цей прийом, щоб підлаштовуватися під різний контент — від короткого повідомлення до карти з маршрутом.
| Тип контенту | InitialContentSizeRatio | preferredContentSize (по коду) |
|---|---|---|
| Текст з однією кнопкою | 0.2 | 150x320 |
| Карта з маршрутом | 0.8 | 400x320 |
| Форма введення | 0.5 | 300x320 |
Медіа-вкладення доступні через notification.request.content.attachments. Якщо Notification Service Extension попередньо завантажив зображення, Content Extension відобразить його в кастомному UIImageView без системного прев'ю.
Чому не можна використовувати мережеві запити?
Це не баг, а усвідомлене обмеження sandbox. Дані повинні приходити в payload (до 4KB для APNs) або через attachment. Якщо потрібен live-рахунок із матчу, оновлюйте UI через UNUserNotificationCenter.current().getDeliveredNotifications() — ми так робили для спортивного додатку, і це спрацювало.
Інтерактивні елементи (UIButton, UITextField) підтримуються, але тапи обробляються через didReceive(_:completionHandler:), а не через IBAction. Ця деталь зламала перший же UIKit-проект новачка. У SwiftUI легше: використовуємо onTapGesture з передачею дії через замикання.
Порівняння підходів: UIKit vs SwiftUI
| Характеристика | UIKit | SwiftUI |
|---|---|---|
| Об'єм коду | ~150 рядків | ~50 рядків |
| Складність анімацій | Середня | Низька (withAnimation) |
| Підтримка iOS | 10+ | 16+ |
| Інтерактивність | Через delegation | Через зв'язування |
| Швидкість розробки | Повільніше в 2 рази | Швидше (менше шаблонного коду) |
SwiftUI-рішення займає в 3 рази менше рядків і простіше в підтримці, але вимагає iOS 16+.
Як оновлювати дані без мережі?
Використовуйте App Groups для спільного сховища. Notification Service Extension може зберегти актуальні дані в shared container, а Content Extension — прочитати їх перед відображенням. Альтернатива — передавати невеликий флаг в payload і підвантажувати статичний контент із бандлу.
Приклад payload для карти доставки
{ "aps": { "alert": { "title": "Замовлення #123", "body": "Кур'єр виїхав" }, "category": "delivery_map" }, "latitude": 53.9, "longitude": 27.56, "order_id": "123" } Що входить в роботу «під ключ»
- Створення таргету Notification Content Extension
- Кастомний UI (XIB, UIKit або SwiftUI) з адаптивною висотою
- Реєстрація
UNNotificationCategoryз action buttons в основному додатку - Обробка action responses і передача управління додатку
- Налаштування App Groups для доступу до shared даних
- Тестування на симуляторі та реальному пристрої через TestFlight
Процес роботи
- Аналітика — визначаємо типи сповіщень, payload-структуру, категорії. Оцінюємо цільові iOS-версії.
- Проектування — UI/UX прототип з урахуванням обмежень (без мережі, 4KB). Вибираємо UIKit або SwiftUI.
- Реалізація — пишемо код Extension + інтеграція з основним додатком. Налаштовуємо provisioning profiles.
- Тестування — перевірка на різних пристроях та iOS-версіях, включаючи симуляцію push-сповіщень.
- Деплой — підготовка App Store Connect, публікація через TestFlight, фінальне рев'ю.
Строки та гарантія
Базовий кастомний UI для одного типу сповіщень: від 1 до 2 днів. З кількома категоріями, інтерактивними елементами та інтеграцією з Service Extension: від 2 до 3 днів. Гарантуємо, що рішення пройде рев'ю App Store (Section 4.2). Наші інженери мають досвід 5+ років в iOS.
Типові помилки при реалізації
- Забути вказати
UNNotificationExtensionCategoryв Info.plist — розширення не спрацює. - Використовувати пряме мережеве з'єднання — падіння в рантаймі.
- Намагатися змінити розмір через constraints без урахування
preferredContentSize— отримаєте обрізаний UI. - Не синхронізувати ідентифікатори категорій між основним додатком та Extension.
Детальніше про можливості Content Extension в офіційній документації Apple.
Оцінимо ваш проект безкоштовно — зв'яжіться з нами. Замовте інтеграцію під ключ, і ми реалізуємо кастомний UI для будь-яких push-сповіщень з урахуванням всіх обмежень iOS.







