react-native-push-notification / ios

React Native Push Notification API for iOS.
MIT License
753 stars 288 forks source link

react native expo #333

Open saqlain455 opened 3 years ago

saqlain455 commented 3 years ago

Invariant Violation: Native module cannot be null. how I can solve this issue

akinncar commented 3 years ago

this package is not compatible with expo, try expo-notifications

berkaygurbuz commented 2 years ago

Clean your node modules and remove react-native-push-notification/ios package. You can eject your expo project to bare workflow using expo eject command. After that install package and follow guide.

charleschenster commented 1 year ago

You can actually have an expo managed build by doing the following

  1. In your app.json, edit your ios.infoPlist to include the following:
    {
    ...
    "ios": {
    "infoPlist": {
      ...
      "UIBackgroundModes": ["remote-notification", ...]
    }
    }
  2. Create a custom plugin to modify your AppDelegate.mm per the readme's instructions
  3. Create a custom base modifier to enable plugins to modify the AppDelegate.h. Then create another custom plugin to modify AppDelegate.h

This is a helpful article on how to create a custom plugin

wojtekmaj commented 1 week ago

Soooo... Based on the instructions above, I got this. Provided without any guarantees. Enjoy.

import fs from 'node:fs';
import { withAppDelegate, withMod, BaseMods, IOSConfig } from 'expo/config-plugins';
import { toMerged } from 'es-toolkit';

import type { ExpoConfig } from 'expo/config';
import type { ConfigPlugin, ExportedConfig, Mod } from 'expo/config-plugins';

/**
 * A plugin which adds new base modifiers to the prebuild config.
 */
function withAppDelegateHeaderBaseMod(config: ExportedConfig) {
  return BaseMods.withGeneratedBaseMods<'appDelegateHeader'>(config, {
    platform: 'ios',
    providers: {
      // Append a custom rule to supply AppDelegate header data to mods on `mods.ios.appDelegateHeader`
      appDelegateHeader: BaseMods.provider<IOSConfig.Paths.AppDelegateProjectFile>({
        // Get the local filepath that should be passed to the `read` method.
        getFilePath({ modRequest: { projectRoot } }) {
          const filePath = IOSConfig.Paths.getAppDelegateFilePath(projectRoot);

          // Replace the .m/.mm with a .h
          if (filePath.endsWith('.m') || filePath.endsWith('.mm')) {
            return `${filePath.slice(0, filePath.lastIndexOf('.'))}.h`;
          }

          throw new Error(`Could not locate a valid AppDelegate.h at root: "${projectRoot}"`);
        },
        // Read the input file from the filesystem.
        async read(filePath) {
          return IOSConfig.Paths.getFileInfo(filePath);
        },
        // Write the resulting output to the filesystem.
        async write(filePath: string, { modResults: { contents } }) {
          await fs.promises.writeFile(filePath, contents);
        },
      }),
    },
  });
}

const withAppDelegateHeader: ConfigPlugin<Mod<IOSConfig.Paths.AppDelegateProjectFile>> = (
  config,
  action,
) => {
  return withMod(config, {
    platform: 'ios',
    mod: 'appDelegateHeader',
    action,
  });
};

const withPushNotificationsIOS: ConfigPlugin = (config) => {
  const configWithAppDelegateHeader = withAppDelegateHeader(config, (appDelegateHeader) => {
    appDelegateHeader.modResults.contents = appDelegateHeader.modResults.contents
      .split('\n')
      .toSpliced(
        appDelegateHeader.modResults.contents.split('\n').indexOf('#import <Expo/Expo.h>') + 1,
        0,
        '#import <UserNotifications/UNUserNotificationCenter.h>',
      )
      .join('\n');

    appDelegateHeader.modResults.contents = appDelegateHeader.modResults.contents
      .split('\n')
      .toSpliced(
        appDelegateHeader.modResults.contents
          .split('\n')
          .indexOf('@interface AppDelegate : EXAppDelegateWrapper'),
        1,
        '@interface AppDelegate : EXAppDelegateWrapper <UNUserNotificationCenterDelegate>',
      )
      .join('\n');

    return appDelegateHeader;
  });

  const configWithAppDelegate = withAppDelegate(configWithAppDelegateHeader, (appDelegate) => {
    appDelegate.modResults.contents = appDelegate.modResults.contents
      .split('\n')
      .toSpliced(
        1,
        0,
        `#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>`,
      )
      .join('\n');

    appDelegate.modResults.contents = appDelegate.modResults.contents
      .split('\n')
      .toSpliced(
        appDelegate.modResults.contents
          .split('\n')
          .indexOf(
            '  return [super application:application didFinishLaunchingWithOptions:launchOptions];',
          ),
        0,
        `  // Define UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
`,
      )
      .join('\n');

    appDelegate.modResults.contents = appDelegate.modResults.contents
      .split('\n')
      .toSpliced(
        appDelegate.modResults.contents
          .split('\n')
          .indexOf(
            '  return [super application:application didFinishLaunchingWithOptions:launchOptions];',
          ) + 2,
        0,
        `
// Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}`,
      )
      .join('\n');

    appDelegate.modResults.contents = appDelegate.modResults.contents
      .replace(
        'return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];',
        '[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];',
      )
      .replace(
        'return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];',
        '[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];',
      )
      .replace(
        'return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];',
        '[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];',
      );

    appDelegate.modResults.contents = appDelegate.modResults.contents
      .split('\n')
      .toSpliced(
        appDelegate.modResults.contents.split('\n').indexOf('@end'),
        0,
        `// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
`,
      )
      .join('\n');

    return appDelegate;
  });

  return toMerged(configWithAppDelegate, {
    ios: {
      entitlements: {
        'aps-environment': process.env.NODE_ENV === 'production' ? 'production' : 'development',
      },
    },
    plugins: [withAppDelegateHeaderBaseMod],
  }) satisfies ExpoConfig;
};