capacitor-push-notifications
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePush Notifications in Capacitor
Capacitor应用中的推送通知
Implement push notifications for iOS and Android using Firebase and APNs.
通过Firebase和APNs为iOS和Android平台的Capacitor应用实现推送通知功能。
When to Use This Skill
何时使用本指南
- User wants push notifications
- User needs FCM setup
- User asks about APNs
- User has notification issues
- User wants rich notifications
- 用户需要为应用添加推送通知功能
- 用户需要配置FCM服务
- 用户咨询APNs相关问题
- 用户遇到推送通知故障
- 用户需要实现富媒体推送通知
Quick Start
快速开始
Install Plugin
安装插件
bash
bun add @capacitor/push-notifications
bunx cap syncbash
bun add @capacitor/push-notifications
bunx cap syncBasic Implementation
基础实现
typescript
import { PushNotifications } from '@capacitor/push-notifications';
async function initPushNotifications() {
// Request permission
const permission = await PushNotifications.requestPermissions();
if (permission.receive === 'granted') {
// Register for push
await PushNotifications.register();
}
// Get FCM token
PushNotifications.addListener('registration', (token) => {
console.log('Push token:', token.value);
// Send token to your server
sendTokenToServer(token.value);
});
// Handle registration error
PushNotifications.addListener('registrationError', (error) => {
console.error('Registration error:', error);
});
// Handle incoming notification (foreground)
PushNotifications.addListener('pushNotificationReceived', (notification) => {
console.log('Notification received:', notification);
// Show in-app notification
showInAppNotification(notification);
});
// Handle notification tap
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
console.log('Notification action:', action);
// Navigate based on notification data
handleNotificationTap(action.notification);
});
}typescript
import { PushNotifications } from '@capacitor/push-notifications';
async function initPushNotifications() {
// 请求推送权限
const permission = await PushNotifications.requestPermissions();
if (permission.receive === 'granted') {
// 注册推送服务
await PushNotifications.register();
}
// 获取FCM令牌
PushNotifications.addListener('registration', (token) => {
console.log('推送令牌:', token.value);
// 将令牌发送至后端服务
sendTokenToServer(token.value);
});
// 处理注册错误
PushNotifications.addListener('registrationError', (error) => {
console.error('注册失败:', error);
});
// 处理前台收到的推送通知
PushNotifications.addListener('pushNotificationReceived', (notification) => {
console.log('收到通知:', notification);
// 在应用内显示通知
showInAppNotification(notification);
});
// 处理通知点击事件
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
console.log('通知操作:', action);
// 根据通知内容跳转页面
handleNotificationTap(action.notification);
});
}Firebase Setup
Firebase配置
1. Create Firebase Project
1. 创建Firebase项目
- Go to https://console.firebase.google.com
- Create new project
- Add iOS and Android apps
- 访问 https://console.firebase.google.com
- 创建新项目
- 添加iOS和Android应用
2. Android Configuration
2. Android平台配置
Download to
google-services.jsonandroid/app/groovy
// android/build.gradle
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.0'
}
}groovy
// android/app/build.gradle
apply plugin: 'com.google.gms.google-services'
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.7.0')
implementation 'com.google.firebase:firebase-messaging'
}将下载至目录
google-services.jsonandroid/app/groovy
// android/build.gradle
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.0'
}
}groovy
// android/app/build.gradle
apply plugin: 'com.google.gms.google-services'
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.7.0')
implementation 'com.google.firebase:firebase-messaging'
}3. iOS Configuration
3. iOS平台配置
Download to
GoogleService-Info.plistios/App/App/ruby
undefined将下载至目录
GoogleService-Info.plistios/App/App/ruby
undefinedios/App/Podfile
ios/App/Podfile
pod 'Firebase/Messaging'
```swift
// ios/App/App/AppDelegate.swift
import Firebase
import FirebaseMessaging
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Messaging.messaging().apnsToken = deviceToken
}
}pod 'Firebase/Messaging'
```swift
// ios/App/App/AppDelegate.swift
import Firebase
import FirebaseMessaging
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Messaging.messaging().apnsToken = deviceToken
}
}4. iOS Capabilities
4. iOS平台权限配置
In Xcode:
- Select App target
- Signing & Capabilities
- Add "Push Notifications"
- Add "Background Modes" > "Remote notifications"
在Xcode中:
- 选择应用目标
- 进入"Signing & Capabilities"页面
- 添加"Push Notifications"权限
- 添加"Background Modes"并勾选"Remote notifications"
APNs Key Setup (iOS)
APNs密钥配置(iOS)
Create APNs Key
创建APNs密钥
- Go to https://developer.apple.com/account
- Certificates, IDs & Profiles
- Keys > Create Key
- Enable Apple Push Notifications service (APNs)
- Download .p8 file
- 访问 https://developer.apple.com/account
- 进入"Certificates, IDs & Profiles"
- 选择"Keys" > "Create Key"
- 启用Apple Push Notifications service(APNs)
- 下载.p8格式的密钥文件
Add to Firebase
关联至Firebase
- Firebase Console > Project Settings
- Cloud Messaging tab
- iOS app configuration
- Upload APNs Authentication Key (.p8)
- Enter Key ID and Team ID
- 进入Firebase控制台 > 项目设置
- 切换至"Cloud Messaging"标签页
- 找到iOS应用配置项
- 上传APNs认证密钥(.p8格式)
- 输入密钥ID和团队ID
Sending Notifications
发送推送通知
Firebase Admin SDK (Node.js)
Firebase Admin SDK(Node.js)
typescript
import admin from 'firebase-admin';
// Initialize
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// Send to single device
async function sendToDevice(token: string) {
await admin.messaging().send({
token,
notification: {
title: 'Hello!',
body: 'You have a new message',
},
data: {
type: 'message',
messageId: '123',
},
android: {
priority: 'high',
notification: {
channelId: 'messages',
icon: 'ic_notification',
color: '#4285f4',
},
},
apns: {
payload: {
aps: {
badge: 1,
sound: 'default',
},
},
},
});
}
// Send to topic
async function sendToTopic(topic: string) {
await admin.messaging().send({
topic,
notification: {
title: 'Breaking News',
body: 'Something important happened',
},
});
}
// Send to multiple devices
async function sendToMultiple(tokens: string[]) {
await admin.messaging().sendEachForMulticast({
tokens,
notification: {
title: 'Update',
body: 'New features available',
},
});
}typescript
import admin from 'firebase-admin';
// 初始化SDK
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// 向单个设备发送通知
async function sendToDevice(token: string) {
await admin.messaging().send({
token,
notification: {
title: '你好!',
body: '你有一条新消息',
},
data: {
type: 'message',
messageId: '123',
},
android: {
priority: 'high',
notification: {
channelId: 'messages',
icon: 'ic_notification',
color: '#4285f4',
},
},
apns: {
payload: {
aps: {
badge: 1,
sound: 'default',
},
},
},
});
}
// 向主题订阅者发送通知
async function sendToTopic(topic: string) {
await admin.messaging().send({
topic,
notification: {
title: '突发新闻',
body: '发生了重要事件',
},
});
}
// 向多个设备发送通知
async function sendToMultiple(tokens: string[]) {
await admin.messaging().sendEachForMulticast({
tokens,
notification: {
title: '更新提示',
body: '应用已推出新功能',
},
});
}HTTP v1 API
HTTP v1 API方式
bash
curl -X POST \
'https://fcm.googleapis.com/v1/projects/YOUR_PROJECT/messages:send' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"message": {
"token": "DEVICE_TOKEN",
"notification": {
"title": "Hello",
"body": "World"
}
}
}'bash
curl -X POST \
'https://fcm.googleapis.com/v1/projects/YOUR_PROJECT/messages:send' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"message": {
"token": "DEVICE_TOKEN",
"notification": {
"title": "Hello",
"body": "World"
}
}
}'Advanced Features
进阶功能
Notification Channels (Android)
Android通知渠道
typescript
import { PushNotifications } from '@capacitor/push-notifications';
// Create channel
await PushNotifications.createChannel({
id: 'messages',
name: 'Messages',
description: 'Message notifications',
importance: 5, // Max importance
visibility: 1, // Public
sound: 'notification.wav',
vibration: true,
lights: true,
lightColor: '#FF0000',
});
// Delete channel
await PushNotifications.deleteChannel({ id: 'old-channel' });
// List channels
const channels = await PushNotifications.listChannels();typescript
import { PushNotifications } from '@capacitor/push-notifications';
// 创建通知渠道
await PushNotifications.createChannel({
id: 'messages',
name: '消息通知',
description: '消息类通知渠道',
importance: 5, // 最高优先级
visibility: 1, // 公开可见
sound: 'notification.wav',
vibration: true,
lights: true,
lightColor: '#FF0000',
});
// 删除通知渠道
await PushNotifications.deleteChannel({ id: 'old-channel' });
// 列出所有通知渠道
const channels = await PushNotifications.listChannels();Topic Subscription
主题订阅
typescript
// Subscribe to topic
await PushNotifications.addListener('registration', async () => {
// Subscribe to topics based on user preferences
const messaging = getMessaging();
await subscribeToTopic(messaging, 'news');
await subscribeToTopic(messaging, 'promotions');
});typescript
// 注册成功后订阅主题
PushNotifications.addListener('registration', async () => {
// 根据用户偏好订阅主题
const messaging = getMessaging();
await subscribeToTopic(messaging, 'news');
await subscribeToTopic(messaging, 'promotions');
});Rich Notifications (iOS)
iOS富媒体通知
swift
// ios/App/NotificationService/NotificationService.swift
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
contentHandler(request.content)
return
}
// Add image
if let imageUrl = request.content.userInfo["image"] as? String,
let url = URL(string: imageUrl) {
downloadImage(url: url) { attachment in
if let attachment = attachment {
mutableContent.attachments = [attachment]
}
contentHandler(mutableContent)
}
} else {
contentHandler(mutableContent)
}
}
}swift
// ios/App/NotificationService/NotificationService.swift
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
contentHandler(request.content)
return
}
// 添加图片附件
if let imageUrl = request.content.userInfo["image"] as? String,
let url = URL(string: imageUrl) {
downloadImage(url: url) { attachment in
if let attachment = attachment {
mutableContent.attachments = [attachment]
}
contentHandler(mutableContent)
}
} else {
contentHandler(mutableContent)
}
}
}Notification Actions
通知操作按钮
typescript
// Handle action buttons
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
switch (action.actionId) {
case 'reply':
// Handle reply action
const input = action.inputValue;
sendReply(input);
break;
case 'dismiss':
// Handle dismiss
break;
default:
// Handle tap
navigateToContent(action.notification.data);
}
});typescript
// 处理通知操作按钮事件
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
switch (action.actionId) {
case 'reply':
// 处理回复操作
const input = action.inputValue;
sendReply(input);
break;
case 'dismiss':
// 处理关闭操作
break;
default:
// 处理通知点击跳转
navigateToContent(action.notification.data);
}
});Background Handling
后台处理
Data-Only Notifications
纯数据通知
typescript
// Server-side: Send data-only message
{
"to": "DEVICE_TOKEN",
"data": {
"type": "sync",
"action": "refresh"
}
// No "notification" key = data-only
}kotlin
// android/app/src/main/java/.../FirebaseService.kt
class FirebaseService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
// Handle data message in background
message.data["type"]?.let { type ->
when (type) {
"sync" -> performBackgroundSync()
"update" -> checkForUpdates()
}
}
}
}typescript
// 服务端:发送纯数据通知
{
"to": "DEVICE_TOKEN",
"data": {
"type": "sync",
"action": "refresh"
}
// 不包含"notification"字段即为纯数据通知
}kotlin
// android/app/src/main/java/.../FirebaseService.kt
class FirebaseService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
// 在后台处理数据消息
message.data["type"]?.let { type ->
when (type) {
"sync" -> performBackgroundSync()
"update" -> checkForUpdates()
}
}
}
}Local Notifications Fallback
本地通知降级方案
typescript
import { LocalNotifications } from '@capacitor/local-notifications';
// Show local notification when in foreground
PushNotifications.addListener('pushNotificationReceived', async (notification) => {
await LocalNotifications.schedule({
notifications: [
{
id: Date.now(),
title: notification.title || '',
body: notification.body || '',
extra: notification.data,
},
],
});
});typescript
import { LocalNotifications } from '@capacitor/local-notifications';
// 应用处于前台时显示本地通知
PushNotifications.addListener('pushNotificationReceived', async (notification) => {
await LocalNotifications.schedule({
notifications: [
{
id: Date.now(),
title: notification.title || '',
body: notification.body || '',
extra: notification.data,
},
],
});
});Best Practices
最佳实践
Permission Handling
权限处理
typescript
async function requestNotificationPermission() {
const { receive } = await PushNotifications.checkPermissions();
if (receive === 'prompt') {
// Show explanation first
const shouldRequest = await showPermissionExplanation();
if (shouldRequest) {
const result = await PushNotifications.requestPermissions();
return result.receive === 'granted';
}
return false;
}
if (receive === 'denied') {
// Guide user to settings
showSettingsPrompt();
return false;
}
return receive === 'granted';
}typescript
async function requestNotificationPermission() {
const { receive } = await PushNotifications.checkPermissions();
if (receive === 'prompt') {
// 先向用户说明通知用途
const shouldRequest = await showPermissionExplanation();
if (shouldRequest) {
const result = await PushNotifications.requestPermissions();
return result.receive === 'granted';
}
return false;
}
if (receive === 'denied') {
// 引导用户前往设置开启权限
showSettingsPrompt();
return false;
}
return receive === 'granted';
}Token Refresh
令牌刷新处理
typescript
// Handle token refresh
PushNotifications.addListener('registration', async (token) => {
const oldToken = await getStoredToken();
if (oldToken !== token.value) {
// Token changed, update server
await updateServerToken(oldToken, token.value);
await storeToken(token.value);
}
});typescript
// 处理令牌刷新事件
PushNotifications.addListener('registration', async (token) => {
const oldToken = await getStoredToken();
if (oldToken !== token.value) {
// 令牌变更,更新服务端记录
await updateServerToken(oldToken, token.value);
await storeToken(token.value);
}
});Error Handling
错误处理
typescript
PushNotifications.addListener('registrationError', (error) => {
console.error('Push registration failed:', error);
// Log to analytics
analytics.logEvent('push_registration_failed', {
error: error.error,
});
// Retry with backoff
scheduleRetry();
});typescript
PushNotifications.addListener('registrationError', (error) => {
console.error('推送注册失败:', error);
// 记录至分析平台
analytics.logEvent('push_registration_failed', {
error: error.error,
});
// 退避重试
scheduleRetry();
});Troubleshooting
故障排查
iOS Not Receiving
iOS无法接收通知
- Check APNs key in Firebase
- Verify Push Notifications capability
- Check provisioning profile
- Verify device token format
- Test with Firebase Console
- 检查Firebase中的APNs密钥配置
- 确认已添加Push Notifications权限
- 检查配置文件有效性
- 验证设备令牌格式
- 使用Firebase控制台测试推送
Android Not Receiving
Android无法接收通知
- Verify google-services.json
- Check notification channel exists
- Verify FCM token
- Check battery optimization
- Test with Firebase Console
- 验证google-services.json文件正确性
- 检查通知渠道是否存在
- 验证FCM令牌有效性
- 检查电池优化设置
- 使用Firebase控制台测试推送
Common Issues
常见问题
| Issue | Solution |
|---|---|
| No token | Check permissions, network |
| Foreground only | Implement background handler |
| Delayed delivery | Use high priority, data-only |
| No sound | Configure notification channel |
| Badge not updating | Set badge in payload |
| 问题 | 解决方案 |
|---|---|
| 无法获取令牌 | 检查权限设置及网络连接 |
| 仅前台能接收通知 | 实现后台消息处理逻辑 |
| 通知延迟送达 | 使用高优先级或纯数据通知 |
| 通知无声音 | 配置通知渠道的声音设置 |
| 角标未更新 | 在推送负载中设置角标数值 |
Resources
参考资源
- Capacitor Push Notifications: https://capacitorjs.com/docs/apis/push-notifications
- Firebase Cloud Messaging: https://firebase.google.com/docs/cloud-messaging
- APNs Documentation: https://developer.apple.com/documentation/usernotifications
- Capacitor Push Notifications文档: https://capacitorjs.com/docs/apis/push-notifications
- Firebase Cloud Messaging文档: https://firebase.google.com/docs/cloud-messaging
- APNs官方文档: https://developer.apple.com/documentation/usernotifications