Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect whether React Native iOS app was opened via push notification

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.

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

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?

like image 910
Mark Amery Avatar asked Dec 17 '15 14:12

Mark Amery


2 Answers

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. 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).

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

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;
  }
});
like image 198
Mark Amery Avatar answered Sep 21 '22 17:09

Mark Amery


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.

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.

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)

// 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)

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.

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.

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.

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

like image 31
Joshua Pinter Avatar answered Sep 25 '22 17:09

Joshua Pinter