capacitor-push-notifications

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Push 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 sync
bash
bun add @capacitor/push-notifications
bunx cap sync

Basic 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项目

  1. Go to https://console.firebase.google.com
  2. Create new project
  3. Add iOS and Android apps
  1. 访问 https://console.firebase.google.com
  2. 创建新项目
  3. 添加iOS和Android应用

2. Android Configuration

2. Android平台配置

Download
google-services.json
to
android/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.json
下载至
android/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
GoogleService-Info.plist
to
ios/App/App/
ruby
undefined
GoogleService-Info.plist
下载至
ios/App/App/
目录
ruby
undefined

ios/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:
  1. Select App target
  2. Signing & Capabilities
  3. Add "Push Notifications"
  4. Add "Background Modes" > "Remote notifications"
在Xcode中:
  1. 选择应用目标
  2. 进入"Signing & Capabilities"页面
  3. 添加"Push Notifications"权限
  4. 添加"Background Modes"并勾选"Remote notifications"

APNs Key Setup (iOS)

APNs密钥配置(iOS)

Create APNs Key

创建APNs密钥

  1. Go to https://developer.apple.com/account
  2. Certificates, IDs & Profiles
  3. Keys > Create Key
  4. Enable Apple Push Notifications service (APNs)
  5. Download .p8 file
  1. 访问 https://developer.apple.com/account
  2. 进入"Certificates, IDs & Profiles"
  3. 选择"Keys" > "Create Key"
  4. 启用Apple Push Notifications service(APNs)
  5. 下载.p8格式的密钥文件

Add to Firebase

关联至Firebase

  1. Firebase Console > Project Settings
  2. Cloud Messaging tab
  3. iOS app configuration
  4. Upload APNs Authentication Key (.p8)
  5. Enter Key ID and Team ID
  1. 进入Firebase控制台 > 项目设置
  2. 切换至"Cloud Messaging"标签页
  3. 找到iOS应用配置项
  4. 上传APNs认证密钥(.p8格式)
  5. 输入密钥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无法接收通知

  1. Check APNs key in Firebase
  2. Verify Push Notifications capability
  3. Check provisioning profile
  4. Verify device token format
  5. Test with Firebase Console
  1. 检查Firebase中的APNs密钥配置
  2. 确认已添加Push Notifications权限
  3. 检查配置文件有效性
  4. 验证设备令牌格式
  5. 使用Firebase控制台测试推送

Android Not Receiving

Android无法接收通知

  1. Verify google-services.json
  2. Check notification channel exists
  3. Verify FCM token
  4. Check battery optimization
  5. Test with Firebase Console
  1. 验证google-services.json文件正确性
  2. 检查通知渠道是否存在
  3. 验证FCM令牌有效性
  4. 检查电池优化设置
  5. 使用Firebase控制台测试推送

Common Issues

常见问题

IssueSolution
No tokenCheck permissions, network
Foreground onlyImplement background handler
Delayed deliveryUse high priority, data-only
No soundConfigure notification channel
Badge not updatingSet badge in payload
问题解决方案
无法获取令牌检查权限设置及网络连接
仅前台能接收通知实现后台消息处理逻辑
通知延迟送达使用高优先级或纯数据通知
通知无声音配置通知渠道的声音设置
角标未更新在推送负载中设置角标数值

Resources

参考资源