Реализация Rich Push Notifications (изображения, кнопки) в мобильном приложении
Стандартное уведомление — заголовок и текст. Rich Push добавляет изображение, кнопки действий, расширенный текст, иногда видео или GIF. На iOS это требует Notification Service Extension. На Android — нативно через NotificationCompat.BigPictureStyle. Разберём оба пути.
iOS: Notification Service Extension
Без NSE изображение в push на iOS не работает. NSE — отдельный таргет в Xcode, который перехватывает уведомление до показа, скачивает медиа и прикрепляет его как UNNotificationAttachment.
// NotificationService.swift
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
let content = request.content.mutableCopy() as! UNMutableNotificationContent
guard let urlString = content.userInfo["image_url"] as? String,
let url = URL(string: urlString) else {
contentHandler(content)
return
}
downloadImage(from: url) { localURL in
if let localURL,
let attachment = try? UNNotificationAttachment(identifier: "image",
url: localURL) {
content.attachments = [attachment]
}
contentHandler(content)
}
}
private func downloadImage(from url: URL, completion: @escaping (URL?) -> Void) {
URLSession.shared.downloadTask(with: url) { tempURL, _, _ in
completion(tempURL)
}.resume()
}
}
NSE имеет лимит времени выполнения — около 30 секунд. Если за это время медиа не скачалось, iOS покажет уведомление без изображения. Поэтому оптимизация размера медиа важна: рекомендуемый размер изображения — до 1 МБ, формат JPEG или PNG.
Кнопки действий на iOS. Категории уведомлений регистрируются при старте приложения:
let likeAction = UNNotificationAction(identifier: "LIKE", title: "❤️ Лайк", options: [])
let replyAction = UNNotificationAction(identifier: "REPLY", title: "Ответить", options: [.foreground])
let category = UNNotificationCategory(identifier: "POST_CATEGORY",
actions: [likeAction, replyAction],
intentIdentifiers: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
В payload уведомления указываем category: "POST_CATEGORY" — iOS покажет кнопки. Обработка нажатия:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
switch response.actionIdentifier {
case "LIKE":
likePost(id: response.notification.request.content.userInfo["post_id"] as? String)
case "REPLY":
openReplyScreen(for: response.notification.request.content.userInfo)
default:
break
}
completionHandler()
}
Android: BigPictureStyle и Action Buttons
На Android кастомный UI нативно:
val bitmap = BitmapFactory.decodeStream(
URL(imageUrl).openConnection().getInputStream()
)
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(body)
.setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.setSummaryText(body)
)
.addAction(
R.drawable.ic_like,
"Лайк",
getLikePendingIntent(postId)
)
.addAction(
R.drawable.ic_reply,
"Ответить",
getReplyPendingIntent(postId)
)
.build()
Загрузку bitmap нельзя делать на main thread. Либо Glide с Glide.with(context).asBitmap().load(url).submit().get() в отдельном потоке, либо WorkManager task при получении data message.
FCM Data Message (не Notification Message) — правильный подход для Rich Push на Android при приложении на переднем плане. Notification Message Android обрабатывает сам, без вашего кода, и там нельзя прикрепить изображение через стандартный механизм. Data Message всегда идёт в onMessageReceived вашего FirebaseMessagingService.
Форматы медиа
| Платформа | Изображение | Видео | GIF |
|---|---|---|---|
| iOS NSE | JPEG, PNG, GIF (статичный превью), HEIC | MP4 (до 50 МБ) | Нет нативно |
| Android | JPEG, PNG | Нет | Нет нативно |
GIF в iOS-уведомлении — только через .gif файл в NSE, iOS покажет первый кадр как превью. Полная анимация работает только в Notification Content Extension (второй таргет, для кастомного UI при long press).
Типичные ошибки
OneSignal и аналоги автоматически скачивают изображение в NSE через свой SDK-код. Но если у вас кастомный NSE или вы забыли добавить OneSignalNotificationServiceExtension в список таргетов App Groups — изображения не появятся, и ошибки в логах не будет.
На Android: если приложение убито и пришёл FCM с notification + data полем одновременно — Android покажет системное уведомление (без изображения) и onMessageReceived не вызовется. Используйте только data payload для полного контроля.
Сроки
Настройка NSE для iOS, реализация кнопок действий для iOS и Android, кастомный NotificationCompat.Builder с BigPictureStyle — 4–7 рабочих дней.







