检测是否通过推送通知打开了React Native iOS应用程序

时间:2022-09-11 20:50:36

Detect if the app was launched/opened from a push notification describes how to detect whether a native iOS app was opened (that is, either launched or merely made active) via the user tapping a push notification.

检测应用程序是否通过推送通知启动/打开,描述了如何通过用户点击推送通知来检测本机iOS应用程序是否已打开(即启动或仅启动)。

How can I do the same thing in React Native? PushNotificationIOS lets me attach a notification listener...

我怎样才能在React Native中做同样的事情? PushNotificationIOS允许我附加通知监听器...

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

but this fires both when a push notification is received with the application in the foreground, and when I open the app via a push notification.

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

How can I detect the second case in particular?

如何特别检测第二种情况?

3 个解决方案

#1


33  

There are two cases here that need to be detected in different ways:

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

  1. The app has been completely terminated (e.g. by restarting the phone, or by double-tapping home and swiping it off the list of apps running in the background) and is being launched by a user's tap on a push notification. This can be detected (and the notification's data acquired) via the React.PushNotificationIOS.getInitialNotification method.
  2. 该应用程序已完全终止(例如,通过重新启动手机,或通过双击主页并将其从后台运行的应用程序列表中移除),并通过用户点击推送通知来启动。这可以通过React.PushNotificationIOS.getInitialNotification方法检测(并获取通知的数据)。

  3. The app had been suspended and is being made active again by a user's tap on a push notification. Just like in a native app, you can tell that this is happening because iOS passes the tapped notification to your app when it is opening (even if it's an old notification) and causes your notification handler to fire while your app is in UIApplicationStateInactive state (or 'background' state, as React Native's AppStateIOS class calls it).
  4. 应用程序已被暂停,并且用户点击推送通知后再次激活该应用程序。就像在本机应用程序中一样,您可以知道这种情况正在发生,因为iOS在打开时将点击通知传递给您的应用程序(即使它是旧通知)并导致您的通知处理程序在您的应用程序处于UIApplicationStateInactive状态时触发(或“背景”状态,如React Native的AppStateIOS类所调用的那样)。

Code to handle both cases (you can put this in your index.ios.js or somewhere else that's run on app launch):

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

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;
  }
});

#2


8  

For Local Notifications

getInitialNotification does not work with Local Notifications.

Unfortunately, as of 0.28 of React Native, using PushNotificationIOS.getInitialNotification() always returns a null value when being launched by a Local Push Notification.

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

Because of that, you need to catch the push notification as a launchOption in your AppDelegate.m and pass it into React Native as an appProperty.

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

Here's all that you need to receive a Local Push Notification from a cold launch or from the background/inactive state.

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

AppDelegate.m (Native iOS Code)

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 (React Native)

index.ios.js(React Native)

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");
  }
}

Make sure to import PushNotificationIOS and AppState from react-native.

确保从react-native导入PushNotificationIOS和AppState。

I haven't tested this with Remote Push Notifications. Perhaps @MarkAmery's method works just fine with Remote Push Notifications but, unfortunately, as of current state of React Native, this is the only way I was able to get Local Push Notifications from a cold state working.

我没有使用远程推送通知进行测试。也许@MarkAmery的方法可以很好地使用远程推送通知,但不幸的是,从React Native的当前状态来看,这是我能够从冷状态工作获得本地推送通知的唯一方法。

This is highly undocumented in React Native so I have created an issue on their GitHub repo to draw attention to it and hopefully rectify it. If you're dealing with this, go there and give it a thumbs up so it percolates to the top.

这在React Native中是高度无证的,所以我在他们的GitHub repo上创建了一个问题,以引起对它的注意,并希望纠正它。如果您正在处理此问题,请转到此处并竖起大拇指,使其渗透到顶部。

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

#3


0  

The solution I found for this is a bit hacky and relies on some potentially weird behavior I noticed regarding the order that event handlers fire. The app I have been working on needed the same functionality, to detect specifically when I open the app via a Push Notification.

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

What I found was that the handler for PushNotificationIOS fires before the handler for AppStateIOS What this meant was that, if I persisted the foreground/background state to memory/AsyncStorage, I could just check it like so in the PushNotification event handler: if (activity === 'background') and if that was true then I knew that I had just opened the app from a Push Notification.

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

I'm always keeping the AppStateIOS foreground/background activity in memory and on disk (using redux and redux-persist respectively) so I just check that whenever I need to know.

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

This may potentially be too hacky for your purposes, and that behavior might change in the future or it could be localized to just me. Try looking at that solution and see what you think.

对于您的目的而言,这可能过于苛刻,并且该行为可能在将来发生变化,或者可能仅仅局限于我。尝试查看该解决方案,看看您的想法。

#1


33  

There are two cases here that need to be detected in different ways:

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

  1. The app has been completely terminated (e.g. by restarting the phone, or by double-tapping home and swiping it off the list of apps running in the background) and is being launched by a user's tap on a push notification. This can be detected (and the notification's data acquired) via the React.PushNotificationIOS.getInitialNotification method.
  2. 该应用程序已完全终止(例如,通过重新启动手机,或通过双击主页并将其从后台运行的应用程序列表中移除),并通过用户点击推送通知来启动。这可以通过React.PushNotificationIOS.getInitialNotification方法检测(并获取通知的数据)。

  3. The app had been suspended and is being made active again by a user's tap on a push notification. Just like in a native app, you can tell that this is happening because iOS passes the tapped notification to your app when it is opening (even if it's an old notification) and causes your notification handler to fire while your app is in UIApplicationStateInactive state (or 'background' state, as React Native's AppStateIOS class calls it).
  4. 应用程序已被暂停,并且用户点击推送通知后再次激活该应用程序。就像在本机应用程序中一样,您可以知道这种情况正在发生,因为iOS在打开时将点击通知传递给您的应用程序(即使它是旧通知)并导致您的通知处理程序在您的应用程序处于UIApplicationStateInactive状态时触发(或“背景”状态,如React Native的AppStateIOS类所调用的那样)。

Code to handle both cases (you can put this in your index.ios.js or somewhere else that's run on app launch):

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

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;
  }
});

#2


8  

For Local Notifications

getInitialNotification does not work with Local Notifications.

Unfortunately, as of 0.28 of React Native, using PushNotificationIOS.getInitialNotification() always returns a null value when being launched by a Local Push Notification.

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

Because of that, you need to catch the push notification as a launchOption in your AppDelegate.m and pass it into React Native as an appProperty.

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

Here's all that you need to receive a Local Push Notification from a cold launch or from the background/inactive state.

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

AppDelegate.m (Native iOS Code)

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 (React Native)

index.ios.js(React Native)

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");
  }
}

Make sure to import PushNotificationIOS and AppState from react-native.

确保从react-native导入PushNotificationIOS和AppState。

I haven't tested this with Remote Push Notifications. Perhaps @MarkAmery's method works just fine with Remote Push Notifications but, unfortunately, as of current state of React Native, this is the only way I was able to get Local Push Notifications from a cold state working.

我没有使用远程推送通知进行测试。也许@MarkAmery的方法可以很好地使用远程推送通知,但不幸的是,从React Native的当前状态来看,这是我能够从冷状态工作获得本地推送通知的唯一方法。

This is highly undocumented in React Native so I have created an issue on their GitHub repo to draw attention to it and hopefully rectify it. If you're dealing with this, go there and give it a thumbs up so it percolates to the top.

这在React Native中是高度无证的,所以我在他们的GitHub repo上创建了一个问题,以引起对它的注意,并希望纠正它。如果您正在处理此问题,请转到此处并竖起大拇指,使其渗透到顶部。

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

#3


0  

The solution I found for this is a bit hacky and relies on some potentially weird behavior I noticed regarding the order that event handlers fire. The app I have been working on needed the same functionality, to detect specifically when I open the app via a Push Notification.

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

What I found was that the handler for PushNotificationIOS fires before the handler for AppStateIOS What this meant was that, if I persisted the foreground/background state to memory/AsyncStorage, I could just check it like so in the PushNotification event handler: if (activity === 'background') and if that was true then I knew that I had just opened the app from a Push Notification.

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

I'm always keeping the AppStateIOS foreground/background activity in memory and on disk (using redux and redux-persist respectively) so I just check that whenever I need to know.

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

This may potentially be too hacky for your purposes, and that behavior might change in the future or it could be localized to just me. Try looking at that solution and see what you think.

对于您的目的而言,这可能过于苛刻,并且该行为可能在将来发生变化,或者可能仅仅局限于我。尝试查看该解决方案,看看您的想法。