Rich Push Notifications (Push com Imagens)
Este guia descreve como configurar o Notification Service Extension em iOS e o suporte nativo a imagens no Android para exibir push notifications com imagens (rich content) usando o Inngage Flutter SDK
Visão Geral
Por padrão, o sistema operacional entrega notificações sem processar o payload antes de exibi-las. Para anexar uma imagem ao push, é necessário um Notification Service Extension — um processo separado que intercepta o payload, baixa a imagem e a anexa à notificação antes que ela apareça na tela.
| Plataforma | Mecanismo | Requisito mínimo |
|---|---|---|
| iOS | UNNotificationServiceExtension + Firebase SDK helper | iOS 10 / Xcode 14+ |
| Android | Firebase Messaging (nativo) | API 21+ |
iOS
1. Criar o Notification Service Extension no Xcode
- Abra o projeto em Xcode (
ios/Runner.xcworkspace). - Vá em File → New → Target.
- Selecione Notification Service Extension e clique em Next.
- Preencha os campos:
- Product Name:
NotificationService - Language: Swift (recomendado) ou Objective-C
- Team: selecione o mesmo App Team do Runner
- Product Name:
- Clique em Finish e aceite Activate quando perguntado.
O Xcode criará um novo grupo
NotificationService/dentro deios/com os arquivosNotificationService.swifteInfo.plist.
2. Implementar o NotificationService.swift
Substitua o conteúdo gerado pelo Xcode pelo código abaixo, que delega o processamento ao helper do Firebase:
import UserNotifications
import FirebaseMessaging
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard let bestAttemptContent = bestAttemptContent else {
contentHandler(request.content)
return
}
// Baixa e anexa a imagem enviada via `fcm_options.image` no payload do push.
Messaging.serviceExtension().populateNotificationContent(
bestAttemptContent,
withContentHandler: contentHandler
)
}
override func serviceExtensionTimeWillExpire() {
// Entrega a melhor tentativa antes do sistema encerrar a extensão.
if let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Objective-C (caso prefira — arquivos .h e .m):
NotificationService.h
#import <UserNotifications/UserNotifications.h>
@interface NotificationService : UNNotificationServiceExtension
@end
NotificationService.m
#import "NotificationService.h"
#import "FirebaseMessaging.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
withContentHandler:(void (^)(UNNotificationContent *))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
[[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent
withContentHandler:contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
self.contentHandler(self.bestAttemptContent);
}
@end
3. Configurar o Info.plist da Extensão
O arquivo NotificationService/Info.plist deve declarar o ponto de extensão. Verifique se contém:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<!-- Swift: use o nome do módulo como prefixo -->
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
<!-- Objective-C: apenas o nome da classe -->
<!-- <string>NotificationService</string> -->
</dict>
</dict>
</plist>
4. Adicionar Firebase/Messaging ao Podfile
A extensão é um binário separado do Runner e precisa linkar o Firebase individualmente. Adicione o target no ios/Podfile:
target 'NotificationService' do
use_frameworks!
pod 'Firebase/Messaging'
end
Após editar o Podfile, execute:
cd ios && pod install
5. Configurar Bundle ID e Provisioning Profile
- No Xcode, selecione o target NotificationService.
- Em Signing & Capabilities:
- Bundle Identifier: deve ser um sub-bundle do app principal.
- Exemplo: se o app é
com.empresa.meuapp, usecom.empresa.meuapp.NotificationService.
- Exemplo: se o app é
- Team: o mesmo do Runner.
- Deployment Target: igual ou maior ao do Runner (mínimo iOS 10).
- Bundle Identifier: deve ser um sub-bundle do app principal.
- O Provisioning Profile é gerado automaticamente quando Automatically manage signing está ativado.
6. Habilitar Push Notifications no App Principal (Runner)
- Selecione o target Runner.
- Em Signing & Capabilities, clique em + Capability.
- Adicione:
- Push Notifications
- Background Modes → marque Remote notifications
- Verifique que o
Runner.entitlementscontém:
<key>aps-environment</key>
<string>development</string> <!-- ou "production" para release -->
7. Verificar o AppDelegate.swift
O AppDelegate deve registrar o delegate do UNUserNotificationCenter:
import UIKit
import Flutter
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Android
No Android, o Firebase Messaging suporta imagens ricas sem extensão adicional.
1. Enviar o campo image no payload
image no payloadInclua notification.image ou fcm_options.image ao criar o push:
{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Título da notificação",
"body": "Corpo da notificação",
"image": "https://exemplo.com/imagem.jpg"
}
}
}
O Firebase SDK no Android baixa e exibe a imagem automaticamente em notificações do sistema. Notificações recebidas em foreground precisam ser exibidas manualmente — o Inngage SDK gerencia isso internamente.
2. Permissões no AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Payload FCM Completo (iOS + Android)
{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Promoção especial",
"body": "Confira nossa oferta de hoje!"
},
"fcm_options": {
"image": "https://cdn.empresa.com/promo-banner.jpg"
},
"apns": {
"payload": {
"aps": {
"mutable-content": 1
}
},
"fcm_options": {
"image": "https://cdn.empresa.com/promo-banner.jpg"
}
}
}
}
Importante — iOS: o campo
"mutable-content": 1no payload APNS é obrigatório para que o sistema acione o Notification Service Extension. Sem ele, a extensão não é chamada e a imagem não aparece.
Especificações da imagem
| Campo | Valor |
|---|---|
| Protocolo | HTTPS (sem autenticação) |
| Formatos | JPEG, PNG, GIF estático |
| Tamanho máximo | ~1 MB (iOS limita o tempo de download a ~30 s) |
| Proporção recomendada | 2:1 — ex.: 1200 × 600 px |
Checklist de Validação
iOS
- Target
NotificationServicecriado no Xcode -
NotificationService.swiftusaMessaging.serviceExtension().populateNotificationContent -
Info.plistda extensão declaraNSExtensionPointIdentifier = com.apple.usernotifications.service - Podfile inclui
pod 'Firebase/Messaging'no target da extensão epod installexecutado - Bundle ID da extensão é sub-bundle do app principal
- Capability Push Notifications adicionada ao Runner
- Capability Background Modes → Remote notifications adicionada ao Runner
- Payload enviado contém
mutable-content: 1efcm_options.image
Android
- Permissões
INTERNETePOST_NOTIFICATIONSnoAndroidManifest.xml - Payload enviado contém
notification.imageoufcm_options.image - URL da imagem é HTTPS e acessível publicamente
Solução de Problemas
| Sintoma | Causa provável | Solução |
|---|---|---|
| Imagem não aparece no iOS | mutable-content ausente no payload | Adicione "mutable-content": 1 no payload APNS |
| Extensão não é invocada | Bundle ID incorreto ou Provisioning Profile inválido | Verifique Bundle ID e reconfigure Signing no Xcode |
| Crash na extensão | Firebase não linkado na extensão | Adicione pod 'Firebase/Messaging' ao target no Podfile e rode pod install |
| Imagem aparece em background mas não em foreground (Android) | Comportamento esperado do FCM | O SDK exibe internamente via flutter_local_notifications |
pod install falha | Podfile.lock desatualizado | Execute pod repo update && pod install |
Updated about 9 hours ago
