npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xtremepush-expo-plugin

v1.1.1

Published

XtremePush Expo Config Plugin

Downloads

96

Readme

XtremePush Expo Plugin

A config plugin for Expo applications that integrates XtremePush SDK functionality with full React Native module support for both iOS and Android platforms.

Table of Contents

Requirements

System Requirements

  • Node.js: 18.0 or higher
  • Expo SDK: 53.0 or higher
  • React Native: 0.73.0 or higher

iOS Requirements

  • iOS Deployment Target: 13.0 or higher
  • Xcode: 14.0 or higher
  • CocoaPods: Latest version
  • Apple Developer Account with Push Notification capability enabled

Android Requirements

  • Minimum SDK: 21 (Android 5.0)
  • Target SDK: 34 (Android 14)
  • Gradle: 8.0 or higher
  • Google Play Services: Required for FCM

Platform Support

| Platform | Minimum Version | Push Notifications | In-App Messages | Location Services | |----------|----------------|-------------------|-----------------|-------------------| | iOS | 13.0+ | ✅ | ✅ | ✅ | | Android | 5.0+ (API 21) | ✅ | ✅ | ✅ |

Installation

Step 1: Install the Plugin

# Using npm
npm install xtremepush-expo-plugin

# Using yarn
yarn add xtremepush-expo-plugin

Step 2: Configure Your App

Add the plugin to your app.json or app.config.js:

{
  "expo": {
    "name": "YourAppName",
    "plugins": [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_XTREMEPUSH_APP_KEY",
          "iosAppKey": "YOUR_IOS_APP_KEY",
          "androidAppKey": "YOUR_ANDROID_APP_KEY",
          "googleSenderId": "YOUR_FCM_SENDER_ID",
          "enableDebugLogs": true,
          "enableLocationServices": true,
          "enablePushPermissions": true
        }
      ]
    ]
  }
}

Step 3: Prebuild Your Project

After adding the plugin configuration, rebuild your native projects:

# Clean rebuild (recommended for first-time setup)
npx expo prebuild --clean

# For iOS, install CocoaPods dependencies
cd ios && pod install && cd ..

Step 4: Run Your Application

# For iOS
npx expo run:ios

# For Android
npx expo run:android

Platform Setup

iOS Setup

1. Basic Configuration

The plugin automatically configures these capabilities in your iOS project:

  • Push Notifications
  • App Groups (for rich media notifications)

2. App Groups Setup (Required for Rich Media)

For rich media push notifications with images and attachments, you need to configure App Groups:

A. In Apple Developer Console:

  1. Log into your Apple Developer Account

  2. Create App Group:

    • Click "Identifiers" → "+" → "App Groups"
    • Identifier: group.{your-bundle-id}.xtremepush
    • Description: "XtremePush Rich Media Notifications"
    • Example: group.com.yourcompany.yourapp.xtremepush
  3. Enable App Groups for your App ID:

    • Click "Identifiers" → Select your App ID
    • Check "App Groups" capability
    • Click "Configure" → Select your created App Group
    • Save changes
  4. Update Provisioning Profiles:

    • Regenerate and download updated provisioning profiles
    • Install them in Xcode or add to your CI/CD

⚠️ Important: If your iOS build fails after enabling App Groups, you need to regenerate your provisioning profiles. This commonly occurs when App Groups are added to an existing App ID.

Solution:

# Clear your iOS build and regenerate with updated provisioning profiles
npx expo prebuild --clean --platform ios

# Install updated CocoaPods dependencies
cd ios && pod install && cd ..

# Rebuild your project
npx expo run:ios

This ensures your local build uses the updated provisioning profiles that include the App Groups capability.

B. Configuration in Your Plugin:

{
  "expo": {
    "plugins": [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_SENDER_ID",
          "enableRichMedia": true,
          "iosAppGroup": "group.com.yourcompany.yourapp.xtremepush"
        }
      ]
    ]
  }
}

C. EAS Build Configuration (if using EAS):

Add to your app.json for EAS builds:

{
  "expo": {
    "extra": {
      "eas": {
        "projectId": "your-project-id",
        "build": {
          "experimental": {
            "ios": {
              "appExtensions": [
                {
                  "targetName": "XtremePushNotificationServiceExtension",
                  "bundleIdentifier": "com.yourcompany.yourapp.XtremePushNotificationServiceExtension",
                  "entitlements": {
                    "com.apple.security.application-groups": [
                      "group.com.yourcompany.yourapp.xtremepush"
                    ]
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}

4. Verification Steps

After setup, verify your configuration:

# Check if Service Extension was created
ls ios/XtremePushNotificationServiceExtension/

# Verify Podfile includes XtremePush SDK
grep -r "Xtremepush-iOS-SDK" ios/Podfile

# Check App Groups in entitlements
grep -r "com.apple.security.application-groups" ios/

Android Setup

1. Firebase Configuration

  1. Create Firebase Project:

  2. Add Android App:

    • Package name must match your android.package in app.json
    • Download google-services.json
    • Place in your project root (not in android/ folder)
  3. Get FCM Server Key:

    • Project Settings → Cloud Messaging → Server Key
    • Use this as googleSenderId in plugin configuration

2. Permissions

The plugin automatically adds required permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Added if location services enabled -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

Configuration

Advanced Configuration

// app.config.js
export default {
  expo: {
    plugins: [
      [
        "xtremepush-expo-plugin",
        {
          // Required
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_FCM_SENDER_ID",
          
          // Platform-specific keys (optional)
          "iosAppKey": "IOS_SPECIFIC_KEY",
          "androidAppKey": "ANDROID_SPECIFIC_KEY",
          
          // Server configuration (optional)
          "useUsServer": true,
          
          // Features (optional)
          "enableDebugLogs": true,
          "enableLocationServices": false,
          "enablePushPermissions": true,
          "enableCrashReporting": false,
          
          // iOS specific (optional)
          "iosPermissions": {
            "NSLocationWhenInUseUsageDescription": "We need your location to provide relevant offers",
            "NSLocationAlwaysAndWhenInUseUsageDescription": "We need your location to send location-based notifications"
          },
          
          // Android specific (optional)
          "androidIcon": "ic_notification",
          "androidIconColor": "#FF0000"
        }
      ]
    ]
  }
};

Rich Media Configuration

Enable rich media notifications with images and attachments:

// app.config.js
export default {
  expo: {
    ios: {
      bundleIdentifier: "com.yourcompany.yourapp"
    },
    plugins: [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_SENDER_ID",
          
          // Enable rich media
          "enableRichMedia": true,
          
          // iOS App Group (required for rich media)
          "iosAppGroup": "group.com.yourcompany.yourapp.xtremepush",
          
          // Service Extension Configuration
          "extensionTargetName": "XtremePushNotificationServiceExtension",
          "extensionBundleSuffix": "XtremePushNotificationServiceExtension",
          "iosDeploymentTarget": "15.0"
        }
      ]
    ]
  }
};

Configuration Options

| Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | applicationKey | string | Yes | - | Your XtremePush application key | | googleSenderId | string | Yes* | - | FCM Sender ID (*Required for Android) | | iosAppKey | string | No | applicationKey | iOS-specific application key | | androidAppKey | string | No | applicationKey | Android-specific application key | | serverUrl | string | No | Default EU server | Custom XtremePush server URL | | useUsServer | boolean | No | false | Use US data center (sets serverUrl to https://sdk.us.xtremepush.com) | | usServerUrl | string | No | https://sdk.us.xtremepush.com | Custom US server URL (used when useUsServer is true) | | enablePinning | boolean | No | false | Enable SSL certificate pinning (iOS only, required for US region) | | certificatePath | string | No | - | Path to SSL certificate file (.der) for certificate pinning | | enableDebugLogs | boolean | No | false | Enable SDK debug logging | | enableLocationServices | boolean | No | false | Enable location tracking | | enablePushPermissions | boolean | No | true | Auto-request push permissions | | enableRichMedia | boolean | No | false | Enable rich media notifications (iOS) | | iosAppGroup | string | No | Auto-generated | iOS App Group identifier | | extensionTargetName | string | No | XtremePushNotificationServiceExtension | Service Extension target name | | iosDeploymentTarget | string | No | 15.0 | iOS deployment target | | devTeam | string | No | - | Apple Developer Team ID |

Server Configuration (US Region Support)

By default, the XtremePush SDK connects to the EU data center. If your account is hosted on the US data center, you need to configure the server URL.

Option 1: Use US Server Flag (Recommended)

// app.config.js
export default {
  expo: {
    plugins: [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_SENDER_ID",
          "useUsServer": true  // ← Automatically uses US data center
        }
      ]
    ]
  }
};

Option 2: Custom Server URL

// app.config.js
export default {
  expo: {
    plugins: [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_SENDER_ID",
          "serverUrl": "https://sdk.us.xtremepush.com"  // ← Custom URL
        }
      ]
    ]
  }
};

Important Notes:

  • The server URL is configured at build time via native code injection
  • After changing server configuration, run npx expo prebuild --clean
  • Both iOS and Android will use the same server URL
  • If both serverUrl and useUsServer are specified, serverUrl takes precedence

SSL Certificate Pinning (US Region Required)

If your project is provisioned in the US region, you must implement Public Key Pinning for security. This validates that the SSL certificate from the server matches an expected certificate bundled with your app.

Step 1: Obtain the Certificate

Contact XtremePush support to obtain the cert.der file for the US region server.

Step 2: Add Certificate to Your Project

Place the certificate file (e.g., cert.der) in your project root or assets folder:

your-project/
├── assets/
│   └── cert.der         ← SSL certificate here
├── app.json
└── package.json

Step 3: Configure Certificate Pinning

// app.config.js
export default {
  expo: {
    plugins: [
      [
        "xtremepush-expo-plugin",
        {
          "applicationKey": "YOUR_APP_KEY",
          "googleSenderId": "YOUR_SENDER_ID",
          "useUsServer": true,              // ← Enable US server
          "enablePinning": true,             // ← Enable certificate pinning
          "certificatePath": "assets/cert.der"  // ← Path to certificate
        }
      ]
    ]
  }
};

Step 4: Rebuild

npx expo prebuild --clean
cd ios && pod install && cd ..
npx expo run:ios

Certificate Pinning Notes:

  • Currently only supported on iOS (Android support coming soon)
  • Certificate file is automatically copied to the iOS bundle during build
  • The path can be absolute or relative to project root
  • Certificate file must have .der extension
  • If certificate is not found, a warning will be logged but the build will continue

Usage

Import the Module

// Import default export
import Xtremepush from './xtremepush';

// Or import specific functions
import { 
  hitEvent, 
  hitTag, 
  setUser, 
  openInbox,
  requestNotificationPermissions 
} from './xtremepush';

Basic Integration

import { useEffect } from 'react';
import Xtremepush from './xtremepush';

export default function App() {
  useEffect(() => {
    // Set user identifier
    Xtremepush.setUser('[email protected]');
    
    // Track app open event
    Xtremepush.hitEvent('app_opened');
    
    // Request push permissions (iOS only)
    Xtremepush.requestNotificationPermissions();
  }, []);
  
  return <YourApp />;
}

User Management

// Set user ID (email, username, or unique ID)
Xtremepush.setUser("[email protected]");

// Set external ID (e.g., your CRM ID)
Xtremepush.setExternalId("CRM-12345");

Event Tracking

// Track simple events
Xtremepush.hitEvent("purchase_completed");
Xtremepush.hitEvent("article_read");
Xtremepush.hitEvent("level_completed");

// Track tags (user properties)
Xtremepush.hitTag("premium_user");
Xtremepush.hitTag("newsletter_subscriber");

// Track tags with values
Xtremepush.hitTagWithValue("user_level", "gold");
Xtremepush.hitTagWithValue("purchase_amount", "99.99");
Xtremepush.hitTagWithValue("cart_items", "3");

Push Notifications

// Request notification permissions
Xtremepush.requestNotificationPermissions();

// Open message inbox
Xtremepush.openInbox();

Initial Notification Handling

The getInitialNotification() method allows you to capture push notification payloads when your app is opened from a terminated state. This provides a reliable alternative to React Native's Linking.getInitialURL() for notification-driven deep linking.

Basic Usage

import { useEffect, useState } from 'react';
import Xtremepush from './xtremepush';

export default function App() {
  const [notificationData, setNotificationData] = useState(null);

  useEffect(() => {
    const checkInitialNotification = async () => {
      try {
        const payload = await Xtremepush.getInitialNotification();
        
        if (payload) {
          console.log('App opened from notification:', payload);
          setNotificationData(payload);
          
          // Handle the notification data
          handleNotificationPayload(payload);
        }
      } catch (error) {
        console.error('Error getting initial notification:', error);
      }
    };

    checkInitialNotification();
  }, []);

  const handleNotificationPayload = (payload) => {
    // Handle deeplink navigation
    if (payload.deeplink) {
      // Navigate to specific screen
      console.log('Navigate to:', payload.deeplink);
    }
    
    // Track notification interaction
    if (payload.campaignId) {
      Xtremepush.hitEvent('notification_opened', {
        campaignId: payload.campaignId,
        notificationId: payload.id
      });
    }
  };

  return <YourApp />;
}

React Navigation Integration

import { useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Xtremepush from './xtremepush';

const Stack = createStackNavigator();

export default function App() {
  const [initialRoute, setInitialRoute] = useState('Home');
  const [initialParams, setInitialParams] = useState({});

  useEffect(() => {
    const handleInitialNotification = async () => {
      try {
        const payload = await Xtremepush.getInitialNotification();
        
        if (payload && payload.deeplink) {
          // Parse deeplink and set initial route
          const route = parseDeeplink(payload.deeplink);
          if (route) {
            setInitialRoute(route.screen);
            setInitialParams({
              ...route.params,
              notificationData: payload
            });
          }
        }
      } catch (error) {
        console.error('Error handling initial notification:', error);
      }
    };

    handleInitialNotification();
  }, []);

  const parseDeeplink = (deeplink) => {
    // Example: "myapp://product/123" or "myapp://article/456"
    const url = new URL(deeplink);
    const pathSegments = url.pathname.split('/').filter(Boolean);
    
    if (pathSegments.length >= 2) {
      const [section, id] = pathSegments;
      
      switch (section) {
        case 'product':
          return { screen: 'ProductDetail', params: { productId: id } };
        case 'article':
          return { screen: 'ArticleDetail', params: { articleId: id } };
        case 'profile':
          return { screen: 'Profile', params: { userId: id } };
        default:
          return { screen: 'Home', params: {} };
      }
    }
    
    return null;
  };

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName={initialRoute}>
        <Stack.Screen 
          name="Home" 
          component={HomeScreen} 
        />
        <Stack.Screen 
          name="ProductDetail" 
          component={ProductDetailScreen}
          initialParams={initialParams}
        />
        <Stack.Screen 
          name="ArticleDetail" 
          component={ArticleDetailScreen}
          initialParams={initialParams}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

TypeScript Usage

import { useEffect, useState } from 'react';
import Xtremepush from './xtremepush';

// Type definition for notification payload
interface XtremePushNotificationPayload {
  id?: string;
  campaignId?: string;
  title?: string;
  text?: string;
  deeplink?: string;
  data?: { [key: string]: any };
  platform?: 'android' | 'ios';
  receivedAt?: number;
  badge?: number;
  [key: string]: any;
}

export default function App() {
  const [notification, setNotification] = useState<XtremePushNotificationPayload | null>(null);

  useEffect(() => {
    const getInitialNotification = async (): Promise<void> => {
      try {
        const payload: XtremePushNotificationPayload | null = 
          await Xtremepush.getInitialNotification();
        
        if (payload) {
          setNotification(payload);
          
          // Type-safe access to payload properties
          const { deeplink, campaignId, data } = payload;
          
          if (deeplink) {
            handleDeeplink(deeplink);
          }
          
          if (campaignId) {
            trackNotificationOpened(campaignId);
          }
          
          if (data?.customField) {
            handleCustomData(data.customField);
          }
        }
      } catch (error) {
        console.error('Failed to get initial notification:', error);
      }
    };

    getInitialNotification();
  }, []);

  return <YourApp />;
}

Payload Structure

The getInitialNotification() method returns a payload object with the following structure:

{
  // Core notification fields
  "id": "1234567",                    // Notification ID
  "campaignId": "1234567",            // Campaign ID
  "title": "Special Offer!",            // Notification title
  "text": "Check out our latest deals", // Notification message
  "deeplink": "myapp://products/sale",  // Deep link URL
  
  // Platform information
  "platform": "ios",                    // or "android"
  "receivedAt": 1640995200000,          // Timestamp when received
  
  // iOS specific
  "badge": 1,                           // Badge count (iOS only)
  
  // Custom data
  "data": {
    "productId": "12345",
    "category": "electronics",
    "discount": "20%",
    "customField": "customValue"
  }
}

Best Practices

  1. Call Early in App Lifecycle

    // Call in App.js or your root component
    useEffect(() => {
      checkInitialNotification();
    }, []);
  2. Handle Null/Empty Responses

    const payload = await Xtremepush.getInitialNotification();
    if (payload) {
      // Handle notification
    } else {
      // App opened normally (not from notification)
    }
  3. Combine with Navigation

    // Wait for navigation to be ready before handling deeplinks
    if (payload && navigationRef.isReady()) {
      handleDeeplink(payload.deeplink);
    }
  4. Track Notification Interactions

    if (payload) {
      Xtremepush.hitEvent('notification_opened');
      Xtremepush.hitTagWithValue('campaign_id', payload.campaignId);
    }
  5. Error Handling

    try {
      const payload = await Xtremepush.getInitialNotification();
      // Handle payload
    } catch (error) {
      console.error('Notification handling failed:', error);
         
    }

iOS Common Issues

  • Wrong Environment: Ensure using Development cert for testing, Production for App Store
  • Expired Certificates: Certificates are valid for 1 year, renew before expiration
  • Bundle ID Mismatch: Certificate must match app's Bundle ID exactly

Android Common Issues

  • Invalid Server Key: Ensure using Server Key, not Web API Key
  • Package Name Mismatch: Firebase package name must match your app exactly
  • Missing google-services.json: File must be in project root

License

This project is licensed under the MIT License.

MIT License

Copyright (c) 2025 XtremePush