【问题标题】:Detect whether React Native iOS app was opened via push notification检测是否通过推送通知打开了 React Native iOS 应用
【发布时间】:2016-03-24 01:41:45
【问题描述】:

Detect if the app was launched/opened from a push notification 描述了如何通过用户点击推送通知来检测原生 iOS 应用程序是否已打开(即启动或仅激活)。

如何在 React Native 中做同样的事情? PushNotificationIOS 让我附加一个通知监听器...

PushNotificationIOS.addEventListener('notification', function (notification) {
    console.log('got a notification', notification);
});

但是当应用程序在前台收到推送通知时,当我通过推送通知打开应用程序时都会触发。

我怎样才能特别检测到第二种情况?

【问题讨论】:

    标签: ios react-native


    【解决方案1】:

    这里有两种情况需要用不同的方式检测:

    1. 应用程序已完全终止(例如,通过重新启动手机,或通过双击主页并将其从后台运行的应用程序列表中滑出)并且正在通过用户的点击启动在推送通知上。这可以通过React.PushNotificationIOS.getInitialNotification 方法检测到(并获取通知的数据)。
    2. 该应用程序已暂停,并通过用户点击推送通知再次激活。只需like in a native app,您就可以知道这是因为iOS 在打开时将点击的通知传递给您的应用程序(即使它是旧通知),并导致您的通知处理程序在您的应用程序处于UIApplicationStateInactive 状态时触发(或 'background' 状态,正如 React Native 的 AppStateIOS 类所称的那样)。

    处理这两种情况的代码(您可以将其放在您的 index.ios.js 或在应用启动时运行的其他位置):

    import React from 'react';
    import { PushNotificationIOS, AppState } from 'react-native';
    
    function appOpenedByNotificationTap(notification) {
      // This is your handler. The tapped notification gets passed in here.
      // Do whatever you like with it.
      console.log(notification);
    }
    
    PushNotificationIOS.getInitialNotification().then(function (notification) {
      if (notification != null) {
        appOpenedByNotificationTap(notification);
      }
    });
    
    let backgroundNotification;
    
    PushNotificationIOS.addEventListener('notification', function (notification) {
      if (AppState.currentState === 'background') {
        backgroundNotification = notification;
      }
    });
    
    AppState.addEventListener('change', function (new_state) {
      if (new_state === 'active' && backgroundNotification != null) {
        appOpenedByNotificationTap(backgroundNotification);
        backgroundNotification = null;
      }
    });
    

    【讨论】:

    • 另外,您可能希望在状态中存储对处理程序的引用,并在componentWillUnmount 中调用removeEventListener(如果您在组件中执行此操作)
    • PushNotificationIOS.getInitialNotification 返回一个承诺,而不是一个通知对象。文档声明它将返回 null 或通知对象。 (react-native v0.26.3)
    • @Tom 回答已修复,Pull Request 已打开。如果可能的话,如果您能测试我答案中当前的代码是否有效,我将不胜感激,因为我无法这样做。
    • @Tom 恐怕不会——我最好的建议是使用alerts 进行调试,遗憾的是完全严肃。我想这就是我所做的。
    • @MarkAmery - 我认为这在某些情况下不起作用。 LMK 如果我错了。 ` 1) 后台应用程序。 2) 收到通知。 3) 打开应用程序正常而不点击通知。 ` 预期结果:正常打开,没什么特别的实际结果:appOpenedByNotificationTap 被调用
    【解决方案2】:

    本地通知

    getInitialNotification 不适用于本地通知。

    不幸的是,从 React Native 的 0.28 开始,使用 PushNotificationIOS.getInitialNotification() 在由 Local 推送通知启动时总是返回 null 值。

    因此,您需要在 AppDelegate.m 中将推送通知作为 launchOption 捕获,并将其作为 appProperty 传递给 React Native。

    这是您从冷启动从后台/非活动状态接收本地推送通知所需的全部内容。

    AppDelegate.m (原生 iOS 代码)

    // Inside of your didFinishLaunchingWithOptions method...
    
    // Create a Mutable Dictionary to hold the appProperties to pass to React Native.
    NSMutableDictionary *appProperties = [NSMutableDictionary dictionary];
    
    if (launchOptions != nil) {
      // Get Local Notification used to launch application.
      UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    
      if (notification) {
        // Instead of passing the entire Notification, we'll pass the userInfo,
        // where a Record ID could be stored, for example.
        NSDictionary *notificationUserInfo = [notification userInfo];
    
        [ appProperties setObject:notificationUserInfo  forKey:@"initialNotificationUserInfo" ];
      }
    }
    
    // Your RCTRootView stuff...
    
    rootView.appProperties = appProperties;
    

    index.ios.js (反应原生)

    componentDidMount() {
      if (this.props.initialNotificationUserInfo) {
        console.log("Launched from Notification from Cold State");
        // This is where you could get a Record ID from this.props.initialNotificationUserInfo
        // and redirect to the appropriate page, for example.
      }
    
      PushNotificationIOS.addEventListener('localNotification', this._onLocalNotification);
    }
    
    componentWillUnmount() {
      PushNotificationIOS.removeEventListener('localNotification', this._onLocalNotification);
    }
    
    _onLocalNotification( notification ) {
      if (AppState.currentState != 'active') {
        console.log("Launched from Notification from Background or Inactive state.");
      }
      else {
        console.log("Not Launched from Notification");
      }
    }
    

    确保从react-native 导入PushNotificationIOSAppState

    我尚未使用远程推送通知对此进行测试。也许@MarkAmery 的方法适用于远程推送通知,但不幸的是,就 React Native 的当前状态而言,这是我能够从冷状态获得本地推送通知的唯一方法。

    这在 React Native 中是高度无证的,所以我在他们的 GitHub 存储库上创建了 an issue 以引起人们对它的关注并希望能纠正它。如果你正在处理这个问题,那就去那里给它竖起大拇指,让它渗透到顶部。

    https://github.com/facebook/react-native/issues/8580

    【讨论】:

    • 除非在我发布答案后更新中发生了一些变化(恐怕我无法检查),否则这是错误的。如果应用程序在添加后被暂停,然后通过点击通知重新激活,通知事件处理程序将触发,但如果应用程序为终止,然后通过点击通知启动 - 这种情况需要通过不同的方式检测。我已经在my answer 中介绍了这一点。
    • @MarkAmery 哦,好收获!你完全正确。现在只是运行一些测试......我会让你知道我发现了什么。
    • @MarkAmery 经过大约一天的调试,我能够正确处理冷启动的本地推送通知。我尝试了您的方法,但至少对于本地推送通知,它不起作用。我现在也正在使用 React Native 打开一个问题。感谢您的帮助。
    • 仅供参考,React Native GitHub 问题:github.com/facebook/react-native/issues/8580
    • @JoshPinter 我正在尝试让我的 react 本机应用程序响应冷启动的推送通知。当我使用PushNotificationIOS.addEventListener点击推送通知打开应用程序时,我让它工作得很好你在说。从推送通知打开应用程序启动后,我尝试使用PushNotificationIOS.getInitialNotification(),但它返回的只是一个空对象:'launchNotification', { _45: 0, _81: 0, _65: null, _54: null }
    【解决方案3】:

    我为此找到的解决方案有点老套,并且依赖于我注意到的一些关于事件处理程序触发顺序的可能奇怪的行为。我一直在开发的应用程序需要相同的功能,以便在我通过推送通知打开应用程序时进行具体检测。

    我发现PushNotificationIOS 的处理程序在AppStateIOS 的处理程序之前触发这意味着,如果我将前台/后台状态持久保存到内存/AsyncStorage,我可以像这样在PushNotification 事件处理程序:if (activity === 'background'),如果这是真的,那么我知道我刚刚从推送通知中打开了应用程序。

    我总是将 AppStateIOS 前台/后台活动保存在内存和磁盘上(分别使用 redux 和 redux-persist),所以我只需要在需要知道的时候检查一下。

    对于您的目的而言,这可能过于 hacky,并且该行为将来可能会改变,或者它可能仅限于我。尝试查看该解决方案,看看您的想法。

    【讨论】:

    • 谢谢,这为我指明了正确的方向。我认为这是最好的,最坏的情况也不比the equivalent solution for native iOS apps 更hacky,它做的事情完全相同。但是,您可能不知道的一个缺点是,这不能处理应用程序已完全终止并且实际上是通过点击推送通知;为此需要popInitialNotification。我会用一些涵盖这两种情况的代码自行回答。
    • @MarkAmery 感谢您指出后一种情况。这确实是这个 hack 的一大缺点。我现在必须依靠用户从后台打开应用程序,这我绝对不喜欢。我很高兴看到您的解决方案,我必须根据您的想法调整我的解决方案!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    • 2015-11-17
    • 2022-06-17
    • 2016-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多