React Native: Locally Test Push Notifications on iOS

Tags: ios, reactnative, testing, github
Published: 2021-09-17 03:05:28

Recently, I had an opportunity to solve push notification testing locally on iOS in a [React Native](https://reactnative.dev/) app.

## Problem

Testing push notifications is a bit tricky when you are using an iOS simulator. It doesn’t work the same as an Android emulator, where we can test push notifications like we would on a real device. On the iOS simulator, we have to rely on the CLI utility to trigger push notifications manually.

```console
xcrun simctl push <device> <bundle-identifier> <path-to-apns-file>
```

This doesn’t help to test the back-end logic of programmatically triggering the push notification. Also, we want to be able to check if the notification was sent to the correct devices.

## Solution

We used the CLI utility to trigger the iOS notification programmatically with the help of shelljs. Furthermore, we listened to incoming notifications on React Native side to check if the notification was meant for the user in question.

### Step 1: Use shelljs to trigger a push notification programmatically

Make sure your project has shelljs as a dependency in order to use the following code.

The message object is what the APNS payload looks like with tokens additional data.

```javascript
const shell = require('shelljs');
const message = {
  aps: {
    alert: {
      title: 'Test',
      body: 'Test',
    },
    tokens: ['FCM_TOKEN_OF_USERS_QUESTION'],
  }
}
shell.exec(`echo '${JSON.stringify(message)}' | xcrun simctl push booted com.example.pushnotification -`);
```

### Step 2: Register a listener to check if the notification was meant for the user in question

In our React Native app, we added the following code to listen to the local notification clicks. The CLI utility triggers local notifications since there is no concept of remote notifications on the simulator.

```javascript
import PushNotificationIOS, {
  PushNotification,
} from '@react-native-community/push-notification-ios';
interface IIOSLocationNotification {
  aps: {
    alert: {
      title: string;
      body: string;
    };
    tokens: string[];
  };
}
useEffect(() => {
  if (Platform.OS !== 'ios' && !__DEV__) {
    return;
  }
  PushNotificationIOS.addEventListener(
    'localNotification',
    onRemoteNotification,
  );
  return () => {
    PushNotificationIOS.removeEventListener('localNotification');
  };
}, []);
const onRemoteNotification = (notification: PushNotification) => {
  const token = 'FCM_TOKEN_OF_USERS_QUESTION';
  const data = notification.getData() as IIOSLocationNotification;
  if (!token || !data.aps.tokens.length) {
    return;
  }
  Alert.alert(
    data.aps.tokens.includes(token)
      ? 'Yes, this notification is for you'
      : 'No, this notification is not for you',
  );
};
```

## Sample App

I have put together a [sample app](https://github.com/ximxim/rn-ios-push-notification-testing) to showcase this solution in more detail. The project has a very simple express backend and React Native app nested in its own folder.

![1_9luCXbO7r6zP4-Fs0gU4bg](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iiihl8bvkebicyom3qx2.gif)
 
Try out the sample app and feel free to share your thoughts in the comments section.

[Github](https://github.com/ximxim/rn-ios-push-notification-testing)