ios - 重置推送通知/设备注册/实例 ID - 通过 TestFlight 或 App Store 更新应用程序 - Firebase 云消息传递 iOS/React Native Firebase

标签 ios reactjs firebase react-native react-native-ios

一直在对这个问题进行大量研究,首先是我的设置:

  • react 原生:0.61.5
  • react-native-firebase : 5.6.0
  • Firebase 消息 6.15.0

  • 尽管我似乎无法找到解决问题的明确方法,但我的问题非常简单直接。

    我部署了我的应用程序的 4.2 版。 iOS 中的 Firebase Cloud Messaging (FCM) 运行良好。然后我将 4.3 部署到 TestFlight 开始测试。通过 TestFlight 安装 4.3 并且 FCM 停止工作,没有推送通知。如果我删除该应用程序并再次通过 TestFlight 推送通知安装 4.3,则该设备已注册。

    我还可以重现此问题,从最新发布的 App Store 更新我的应用程序的安装版本,这并不奇怪。

    我知道 InstanceId/device token 基于应用程序构建+设备,因此当应用程序更新时 token 更改是有意义的,但当我从 4.2 更新到 4.3 时我的 token 是相同的:
    const fcmToken = await firebase.messaging().getToken(); //same whether 4.2 or 4.3
    

    是的 firebase.messaging().getToken() 在初始创建时被缓存。

    所以我准备收听 token 更改事件:
    firebase.messaging().onTokenRefresh(async () => {
      console.log('======onTokenRefresh=========');
      AsyncStorage.removeItem(FCM_TOKEN_KEY);
      await getToken();
    });
    

    那似乎永远不会开火。我什至在我的应用程序中添加了一个按钮,以尝试在从 TestFlight 或 AppStore 更新版本后强制使用新的 InstanceId 并注册 FCM,这无济于事:
    export async function forceRefresh() {
      console.log('================force a new registration!!!==========');
      AsyncStorage.removeItem(FCM_TOKEN_KEY);
      firebase.messaging().deleteToken();
      firebase.iid().deleteToken();
      registerForPushNotificationsAsync(true);
    } 
    

    不,不起作用。

    所以这个故事的寓意是,当我更新我的应用程序的版本时,我的客户,他们的推送通知工作得很好,当他们将他们的应用程序更新到下一个版本时,他们将不再注册推送通知。

    可能是我一直在研究的线索的引用资料:
  • didRegisterForRemoteNotificationsWithDeviceToken called twice?
  • didRegisterForRemoteNotificationsWithDeviceToken is not getting called Swift 5, Xcode 10.2
  • https://firebase.googleblog.com/2017/01/debugging-firebase-cloud-messaging-on.html
  • https://github.com/firebase/firebase-ios-sdk/issues/2438
  • https://github.com/evollu/react-native-fcm/issues/1100
  • Firebase Cloud Messaging - Handling logout

  • 有趣的是应用程序更新开始,推送通知将由于应用程序更新而停止工作我看到两个不同的 token ,也许第一个是 APN token from Apple :
    2020-01-17 18:48:09.371741-0800 native[4462:1287461] -[RNFirebaseMessaging messaging:didReceiveRegistrationToken:] [Line 86] Received new FCM token: eBBgznWj1FU:APA91bF8vTmpkwcojp4oDSKFzlPDp6ylEIe_WGNzu24SKHS6RR-3xPu2-cX-Qyc8rrMIQMvkCJftT9711ll1WdshBWS4iEpZ3XpiPeTynqM-nvDjpAUUUWJpfT5aeo6G_scDsN9iipwI
    2020-01-17 18:48:09.378382-0800 native[4462:1287621] 6.15.0 - [Firebase/InstanceID][I-IID014012] Invalidating cached token for 255558254149 (*) due to token is no longer fresh.
    

    稍后,我从之前版本的应用程序中加载了推送通知工作的先前 token ,我在日志中看到了工作“默认 token ”:
    2020-01-17 18:48:09.987571-0800 native[4462:1287610] 6.15.0 - [Firebase/InstanceID][I-IID014001] Token fetch successful, token: dUY5psWDnkv2td1kB_t6Gs:APA91bEaREBt07CWiEyGvP4YAGjxmVQmF0IcXgef5XcvL5KWrHsqcxZZ8L9PqwGzKTPFGy6cdmuVXSvg6kDQjj-652jt5_jbbKMhUFTcam_-FeBp2vGZvBjaBd4aAOtQf1m48htQ8d6B, authorizedEntity: 255558254149, scope:*
    2020-01-17 18:48:09.987764-0800 native[4462:1287461] -[RNFirebaseMessaging messaging:didReceiveRegistrationToken:] [Line 86] Received new FCM token: dUY5psWDnkv2td1kB_t6Gs:APA91bEaREBt07CWiEyGvP4YAGjxmVQmF0IcXgef5XcvL5KWrHsqcxZZ8L9PqwGzKTPFGy6cdmuVXSvg6kDQjj-652jt5_jbbKMhUFTcam_-FeBp2vGZvBjaBd4aAOtQf1m48htQ8d6B
    2020-01-17 18:48:09.993088-0800 native[4462:1287610] 6.15.0 - [Firebase/InstanceID][I-IID003010] Successfully fetched default token.
    2020-01-17 18:48:09.993755-0800 native[4462:1287610] 6.15.0 - [Firebase/InstanceID][I-IID003008] Got default token dUY5psWDnkv2td1kB_t6Gs:APA91bEaREBt07CWiEyGvP4YAGjxmVQmF0IcXgef5XcvL5KWrHsqcxZZ8L9PqwGzKTPFGy6cdmuVXSvg6kDQjj-652jt5_jbbKMhUFTcam_-FeBp2vGZvBjaBd4aAOtQf1m48htQ8d6B
    

    有趣的是,在删除当前版本的应用程序并安装最新版本(在之前的日志中是更新版本)之后,我们可以看到相反的过程,首先找到我的当前 token 并认为不再新鲜:
    messaging:didReceiveRegistrationToken:] [Line 86] Received new FCM token: dUY5psWDnkv2td1kB_t6Gs:APA91bEaREBt07CWiEyGvP4YAGjxmVQmF0IcXgef5XcvL5KWrHsqcxZZ8L9PqwGzKTPFGy6cdmuVXSvg6kDQjj-652jt5_jbbKMhUFTcam_-FeBp2vGZvBjaBd4aAOtQf1m48htQ8d6B
    2020-01-17 19:03:06.651179-0800 native[4475:1291698] 6.15.0 - [Firebase/InstanceID][I-IID014012] Invalidating cached token for 255558254149 (*) due to token is no longer fresh.
    

    随后新 token 被加载并被视为默认 token ,我的新安装立即收到 FCM 通知:
    2020-01-17 19:03:07.997209-0800 native[4475:1291564] -[RNFirebaseMessaging messaging:didReceiveRegistrationToken:] [Line 86] Received new FCM token: ebz2ACPpBkg0kGsgs9yF7_:APA91bGErCaPMuLyRk-_BLZXUk8_U6FyxvKHbI0NPgddFWl_-nLZuCc6HbHg8kaLMDJiO7sHFS8THAuV132xgri8uQ9YV4g8zDXJySrKsSTNiDq9HcXpzUQXQlPy8bTaxZ3gyRxyCy3p
    2020-01-17 19:03:08.018870-0800 native[4475:1291684] 6.15.0 - [Firebase/InstanceID][I-IID014001] Token fetch successful, token: ebz2ACPpBkg0kGsgs9yF7_:APA91bGErCaPMuLyRk-_BLZXUk8_U6FyxvKHbI0NPgddFWl_-nLZuCc6HbHg8kaLMDJiO7sHFS8THAuV132xgri8uQ9YV4g8zDXJySrKsSTNiDq9HcXpzUQXQlPy8bTaxZ3gyRxyCy3p, authorizedEntity: 255558254149, scope:*
    2020-01-17 19:03:08.019018-0800 native[4475:1291684] 6.15.0 - [Firebase/InstanceID][I-IID003010] Successfully fetched default token.
    2020-01-17 19:03:08.019065-0800 native[4475:1291684] 6.15.0 - [Firebase/InstanceID][I-IID003008] Got default token ebz2ACPpBkg0kGsgs9yF7_:APA91bGErCaPMuLyRk-_BLZXUk8_U6FyxvKHbI0NPgddFWl_-nLZuCc6HbHg8kaLMDJiO7sHFS8THAuV132xgri8uQ9YV4g8zDXJySrKsSTNiDq9HcXpzUQXQlPy8bTaxZ3gyRxyCy3p
    

    刚刚在日志中发现了这个兴趣声明:

    APNS device token not set before retrieving FCM Token for Sender ID '255558254149'. Notifications to this FCM Token will not be delivered over APNS.Be sure to re-retrieve the FCM token once the APNS device token is set.



    很难相信这种情况会如此持续地发生,但似乎确实如此,任何帮助将不胜感激。

    最佳答案

    好吧,它最终成为了一种竞争条件,我发现了一个很好的提示 here

    Seems with react-native-firebase Firebase.messaging().getToken() will not always return the latest token - use onTokenRefresh instead. My app was saving and using an old token and not updating a new one - very small race condition.

    Simply use Firebase.messaging.onTokenRefresh() as a source of truth to avoid issues with migrating apps. Also to be sure you get a token that works. You may want to delete your token on a migration such as this with v4 -> await Firebase.iid().deleteToken(), or v5 (Firebase.messaging().deleteToken(). Then rely onTokenRefresh to send you a new one to send to your server.



    现在firebase.messaging().deleteToken()当有来自 TestFlight 或 Apple App Store 的应用更新时,上面引用中提到的“足够好”不足以导致 tokenRefresh。

    我不得不调用异步(当涉及到这个问题时,一切都是异步的,这是经验法则):
    firebase.iid().delete();
    

    这个React Native Firebase api 方法删除 InstanceId,这实际上是 Firebase 云消息传递 (FCM) 用于 token 的内容。

    这将触发 onTokenRefresh。所以我所做的是检查应用程序的版本和内部版本号并将其存储在应用程序用户数据(iOS 的 NSDefaults)中,我检查此版本和内部版本是否存在,如果不存在,我会“刷新”设备 token 。这样,这只发生一次。

    我将添加我的代码,希望它可以帮助其他人解决这个问题,基本上,从我的研究来看,使用 React Native Firebase 5.x 这是你必须做的,以在 iOS 应用程序中保持与 Firebase Cloud Messaging 的无缝推送通知注册更新:

    在您的 App.js 中添加以下内容:
       configureFirebaseCloudMessaging = async () => {
        //wire up Firebase Cloud Messaging onTokenRefresh listener
    
        this.fcmOnTokenRefreshUnsubscribe = await firebase.messaging().onTokenRefresh(async fcmToken => {
          console.log('*********************** onTokenRefresh *****************');
          //this is callBack called typically sometime in the future but can be call with app loaded but user not logged in
          //check for that case and exit if there is no accessToken to call APIs
          const accessToken = await AsyncStorage.getItem('access-token');
          if (!accessToken) {
            console.log('************** user is not logged in exit onTokenRefresh do not register device ************');
            return;
          }
          await this.registerDevice(fcmToken, BASE_URL);
          await AsyncStorage.setItem('fcmToken', fcmToken);
          firebase.crashlytics().log(`flushed new fcmToken: ${fcmToken}`);
          console.log('***************** success  account updated with latest token **************');
        });
    
        //Firebase Cloud Messaging time
        await this.requestPushPermission();
        await this.checkFlushv();
      }
    
      checkFlushv = async () => {
        let FLUSHV = `${DeviceInfo.getVersion()}-build-${DeviceInfo.getBuildNumber()}`;
    
        const flush = await AsyncStorage.getItem(FLUSHV);
        console.log('======= checking FLUSHV=========', flush);
        if (flush) {
          console.log('***** device token already been flushed ******');
          return;
        }
    
        const accessToken = await AsyncStorage.getItem('access-token');
        console.log('flushy access token:', accessToken);
        if (!accessToken) {
          console.log('******** user is not logged in do not flush ************');
          return;
        }
    
        //force push notifications, this will fire onTokenRefresh callback
        await firebase.iid().delete();
    
        //iterate all keys and remove other builds to keep tidy and TestFlighters possibly going up and back down build versions for testing
        const keys = await AsyncStorage.getAllKeys();
        const buildKeys = keys.filter(key => {
          return key.indexOf('build') !== -1;
        });
        await AsyncStorage.multiRemove(buildKeys);
    
        //add current build key so no more flushy
        await AsyncStorage.setItem(FLUSHV, FLUSHV);
        console.log(`=================FLUSHV clear: ${FLUSHV}===================`)
      }
    
      registerDevice = async (token, baseUrl) => {
        console.log(`******** registerDevice token: ${token}, baseUrl: ${baseUrl}`);
        let data = {
          device: 'firebase',
          token: token
        }
    
        //register token with  account
        axios.post(`${baseUrl}/myapi/register_device`, data)
          .then(response => {
            return {}
          })
          .catch(err => {
            console.log(err)
            return {}
          })
      }
    

    然后在你的 App.js 中:
      async componentDidUpdate() {
        //a new version of the app could be loaded post/after App.componentDidMount
        await this.checkFlushv();
      }
    
      async componentWillUnmount() {
        console.log('app componentWillUnmount');
        this.fcmOnTokenRefreshUnsubscribe(); //not really sure unsubcribe is needed but keeping tidy
        this.fcmOnTokenRefreshUnsubscribe = null;
      }
    
      async componentDidMount() {
        await this.configureFirebaseCloudMessaging();
      }
    

    关于ios - 重置推送通知/设备注册/实例 ID - 通过 TestFlight 或 App Store 更新应用程序 - Firebase 云消息传递 iOS/React Native Firebase,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59796431/

    相关文章:

    ios - 如何阻止 UILabel 将 "..."替换为省略号

    javascript - 如果元素存在则条件渲染

    javascript - 重定向后如何更新组件的状态?

    java - Firebase 数据库 Android - 将具有不同结构的 datasnapshot 子级映射到 java 对象

    node.js - Firebase - TypeError : admin. auth(...).createUserWithEmailAndPassword 不是函数

    Firebase:在项目中找不到可注册的 Android 客户端

    android - 加密从移动应用程序发送的数据,这可能吗?

    ios - 解析无效 session token (代码 : 209, 版本 : 1. 7.1)

    ios - 为什么 "Use of undeclared type UITableView"发生在 Swift 中?

    javascript - 使用 map 时 react 'cannot read property of undefined'