You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cloud Function发送的iOS通知未收到,Firebase Console测试正常

iOS设备无法接收Cloud Function发送的FCM通知

已完成的验证步骤

  • Xcode中已启用推送通知权限,同时开启后台模式:Background fetch、Remote notifications、Background processing
  • Firebase项目的Cloud Messaging模块已上传APNs Auth Key,且Firebase Cloud Messaging API (V1)处于启用状态
  • Cloud Function已绑定Firebase项目,使用Firebase Admin SDK Service Agent完成配置

核心问题

Cloud Function可正常响应recent_messages集合的文档创建/更新操作,日志返回成功发送的响应(例如textPayload: "Successfully sent message: projects/{Project_ID}/messages/1701549315603249"),但iOS设备完全收不到通知。但使用Firebase Console向同一FCM令牌发送通知时,设备能正常接收并显示。

Xcode中didReceiveRemoteNotification方法设置的断点仅在Firebase Console发送通知时触发,Cloud Function发送时无任何触发迹象。Cloud Function日志仅显示成功信息,目前找不到下一步排查方向。

Cloud Function代码(尝试过两种发送方式)

const gaxios = require('gaxios');

const admin = require('firebase-admin');
const functions = require('firebase-functions');

admin.initializeApp(functions.config().firebase);

let db = admin.firestore();

const { GoogleAuth } = require('google-auth-library');

async function getAccessToken() {
  const auth = new GoogleAuth({
    keyFile: './service-account-key.json',
    scopes: 'https://www.googleapis.com/auth/firebase.messaging',
  });

  const client = await auth.getClient();
  const accessTokenResponse = await client.getAccessToken();
  return accessTokenResponse.token;
}

// onCreate trigger for new messages
exports.onCreateMessage = functions.firestore.document('recent_messages/{messageId}').onCreate(
  (snapshot) => {
      handleNotification(snapshot);
    return handleNotificationPost(snapshot);
  }
);

// onUpdate trigger for message updates
exports.onUpdateMessage = functions.firestore.document('recent_messages/{messageId}').onUpdate(
  (change) => {
      handleNotification(change.after);
    return handleNotificationPost(change.after);
  }
);

// Function to handle sending notifications
async function handleNotification(snapshot) {
  // Fetching the recent message data
  const messageData = snapshot.data();
  const gid = messageData.gid;
  const toId = messageData.toId; 
  const fromId = messageData.fromId;
  const type = messageData.type; 
  const text = messageData.text;

  // Query the 'games' collection
  const gameRef = db.collection('games').doc(gid);
  const gameDoc = await gameRef.get();

  const gameData = gameDoc.data();
  const gameUsers = gameData.gameUsers || [];
  const user = gameUsers.find(u => u.fid === toId);

  if (!user) {
    console.log('User not found');
    return null;
  }

  // Send notification
  if (user.deviceToken) {
    try {
      // Notification details
        const payload = {
          notification: {
            title: "Test Title",
            body: "Test body"
          },
          apns: {
            payload: {
              aps: {
                alert: {
                  title: "Test Title",
                  body: "Test body"
                },
              }
            }
          },
          token: user.deviceToken
        };
      console.log("FCM Token:", user.deviceToken)
      admin.messaging().send(payload)
        .then((response) => {
            console.log('Successfully sent message:', response);
        }).catch((error) => {
            console.log('Error sending message:', error);
          });
      console.log('Notification sent to:', toId);
    } catch (error) {
      console.error('Error sending notification:', error);
    }
  } else {
    console.log('No device token found for user:', toId);
  }
}

// Function to handle sending notifications
async function handleNotificationPost(snapshot) {
  // Fetching the recent message data
  const messageData = snapshot.data();
  const gid = messageData.gid;
  const toId = messageData.toId;
  const fromId = messageData.fromId;
  const type = messageData.type;
  const text = messageData.text;

  // Query the 'games' collection
  const gameRef = db.collection('games').doc(gid);
  const gameDoc = await gameRef.get();

  const gameData = gameDoc.data();
  const gameUsers = gameData.gameUsers || [];
  const user = gameUsers.find(u => u.fid === toId);

  if (!user) {
    console.log('User not found');
    return null;
  }

  // Send notification
  if (user.deviceToken) {
    try {
        const accessToken = await getAccessToken();
        
      // Notification details
        const payload = {
            "message": {
                "token": user.deviceToken,
                "notification": {
                    "title": "New message from ${fromId}",
                    "body": type === "text" ? (text.length <= 100 ? text : text.substring(0, 97) + "...") : "You received an image",
                },
            }
        };
          
        console.log("FCM Token:", user.deviceToken)
        console.log('Access Token:', accessToken);
        const url = 'https://fcm.googleapis.com/v1/projects/{ProjectID}/messages:send';

      // Sending POST request
        const response = await gaxios.request({
            url: 'https://fcm.googleapis.com/v1/projects/{ProjectID}/messages:send',
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${accessToken}`
            },
            data: payload
          });

      console.log('Notification sent to:', toId);
      console.log('Response from POST:', response.data);
    } catch (error) {
      console.error('Error sending notification:', error);
    }
  } else {
    console.log('No device token found for user:', toId);
  }
}

Xcode相关代码

func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult)
                     -> Void) {
        
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }
        
        // Print full message.
        print("Did receive remote notification: \(userInfo)")
        
        completionHandler(UIBackgroundFetchResult.newData)
    }

内容的提问来源于stack exchange,提问作者James Bradley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:45:02