zo0r / react-native-push-notification

React Native Local and Remote Notifications
MIT License
6.76k stars 2.05k forks source link

on iOS onRegister is called several times every second #1389

Closed bneigher closed 4 years ago

bneigher commented 4 years ago

Bug

On production release builds, I've got pretty bad bug where onRegister is spamming my app, destroying my battery life / cpu. I believe I have the correct setup, but perhaps it is indeed operator error? It seems to be happening on all iOS devices.

Environment info

System:
    OS: macOS 10.15.3
    CPU: (8) x64 Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
    Memory: 23.63 MB / 16.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 13.12.0 - ~/.nvm/versions/node/v13.12.0/bin/node
    Yarn: Not Found
    npm: 6.14.4 - ~/.nvm/versions/node/v13.12.0/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.9.1 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 13.4, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
    Android SDK: Not Found
  IDEs:
    Android Studio: 3.6 AI-192.7142.36.36.6392135
    Xcode: 11.4.1/11E503a - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.2 - /usr/bin/javac
    Python: 2.7.16 - /usr/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: ^16.11.0 => 16.13.1
    react-native: ^0.62.2 => 0.62.2
  npmGlobalPackages:
    *react-native*: Not Found

Library version: 3.2.1

Steps To Reproduce

  1. App is booted up, in the onBeforeLift callback of my component (redux-persist/integration/react) I "boot" my push notification instance, which calls configure with all the required methods.
  2. I can see that onRegister gets called, giving me my token, but it calls it several times a second.

Describe what you expected to happen:

  1. I boot up my app, and boot the push notifications with configure
  2. onRegister is called one time, so I can relay my token to my web services
  3. Unless app is closed, onRegister is not called again

App.js

...
   componentDidMount() {
      ...
      if (!__DEV__) PushHolder.boot();
    }
...

PushHolder.js

import { Platform } from 'react-native';
import Config from 'react-native-config';
import PushNotification from 'react-native-push-notification';
import AsyncStorage from '@react-native-community/async-storage';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import Intercom from 'react-native-intercom';
import * as actions from 'app/actions';

import { configuredStore } from 'app/configureStore';
import { cognitoUpdate } from 'api/auth';

import * as types from 'types';

const requestNotificationPermissions = async bool => {
  if (Platform.OS === 'ios') {
    const perms = await PushNotificationIOS.requestPermissions();
    if (!perms.alert && !perms.badge && !perms.sound) {
      return false;
    }
    return true;
  }
  return bool;
};

class PushHolder {
  constructor(args) {}

  static async boot() {
    PushNotification.configure({
      // (optional) Called when Token is generated (iOS and Android)
      onRegister: this.onRegister,

      // (required) Called when a remote or local notification is opened or received
      onNotification: this.onNotification,

      // ANDROID ONLY: GCM or FCM Sender ID (product_number) (optional - not required for local notifications, but is need to receive remote push notifications)
      senderID: '<REDACTED>',

      // IOS ONLY (optional): default: all - Permissions to register.
      permissions: {
        alert: true,
        badge: true,
        sound: true,
      },

      // Should the initial notification be popped automatically
      // default: true
      popInitialNotification: true,

      /**
       * (optional) default: true
       * - Specified if permissions (ios) and token (android and ios) will requested or not,
       * - if not, you must call PushNotificationsHandler.requestPermissions() later
       */
      requestPermissions: configuredStore.store.getState().device.settings
        .pushNotifications,
    });
  }

  static async onRegister(data) {
    window.alert(JSON.stringify(data));
    try {
      await AsyncStorage.setItem(
        'push_token' + Config.AWS_ANALYTICS_ID,
        data.token,
      );

      if (Platform.OS !== 'ios') {
        Intercom.sendTokenToIntercom(data.token);
        Intercom.registerForPush();
      }

      if (configuredStore.store.getState().device.settings.pushNotifications) {
        const perms = await requestNotificationPermissions(
          configuredStore.store.getState().device.settings.pushNotifications,
        );

        if (!perms) {
          await configuredStore.store.dispatch({
            type: types.UPDATE_DEVICE_SETTINGS,
            payload: {
              pushNotifications: false,
            },
          });
          cognitoUpdate(configuredStore.store.getState().auth);
        }
      }
    } catch (err) {
      window.alert(err.toString());
    }
  }

  static async onNotification(notification) {
    console.log('Notification', notification);
    // this.onNotificationOpened(notification)
    if (Platform.OS === 'ios') {
      await configuredStore.store.dispatch(actions.recalculateBadgeNumber());
      // required on iOS only (see fetchCompletionHandler docs: https://facebook.github.io/react-native/docs/pushnotificationios.html)
      notification.finish(PushNotificationIOS.FetchResult.NoData);
    }
  }

  // (required) Called when a remote or local notification is opened or received
  static onNotificationOpened(notification) {
    configuredStore.store.dispatch({
      type: types.RECORD_OPEN_PUSH_NOTIFICATION,
      // payload: {
      //   value: notification._data.data.pinpoint.campaign.campaign_id
      // }
    });
  }

  static requestNotificationPermissions = requestNotificationPermissions;
}

export default PushHolder;

AppDelegate.h

#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@property (nonatomic, strong) UIWindow *window;

@end

AppDelegate.m (all methods were added)

Dallas62 commented 4 years ago

Hi @bneigher Maybe intercom.registerForPush(); that retrigger onRegister by requesting token ?

Dallas62 commented 4 years ago

@bneigher Sorry, I think it's not Intercom but: PushNotificationIOS.requestPermissions() It triggers register, and it's in onRegister. If it's registered, why request them ?

bneigher commented 4 years ago

Oh man, yea I think thats the problem. I was trying to account for the condition in which the user disables push notifications from the iOS settings by re-asking them if they enable it via our app. Operator error indeed. Thanks @Dallas62