Skip to main content
Complete guide to implementing push notifications in React Native with Zixflow using Apple Push Notifications (APNs) or Firebase Cloud Messaging (FCM).
** Track your campaigns:** The SDK automatically tracks delivery, opens, and clicks. For advanced tracking customization and implementation details, see Push Notification Tracking.

Prerequisites

Before implementing push notifications:
  1. Configure push credentials in Zixflow dashboard - Add APNs certificates or FCM Server Key
  2. Complete iOS/Android native setup - Ensure native dependencies are installed
  3. Device tokens must be associated with users - Call identify() before sending notifications
Important Limitation: The Zixflow React Native SDK currently supports deep links and images in push notifications. Custom action buttons require additional native code.

Platform-Specific Requirements

iOS Requirements

  • APNs or FCM: Choose either Apple Push Notification service or Firebase Cloud Messaging
  • Xcode Capabilities: Push Notifications and Background Modes
  • Notification Service Extension: Required for rich push (images, videos)
  • Physical Device: iOS Simulator cannot receive push notifications

Android Requirements

  • Firebase Project: Required for FCM integration
  • google-services.json: Must be in android/app/ directory
  • Minimum API Level 24: Android 7.0 or higher
  • POST_NOTIFICATIONS Permission: Required for Android 13+ (API 33+)

iOS Setup

You can use either APNs (Apple Push Notification service) or FCM (Firebase Cloud Messaging) on iOS.

Step 1: Enable Push Capabilities in Xcode

  1. Open your project’s .xcworkspace file in Xcode
  2. Select your app target
  3. Go to Signing & Capabilities tab
  4. Click + Capability → Add Push Notifications
  5. Click + Capability → Add Background Modes
  6. Enable Remote notifications under Background Modes

Step 2: Choose APNs or FCM

Option A: APNs (Recommended for iOS-only apps) Update your ios/Podfile:
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '13.0'

target 'YourApp' do
  config = use_native_modules!

  use_react_native!(:path => config[:reactNativePath])

  # Add Zixflow with APNs support
  pod 'zixflow-reactnative/apn', :path => '../node_modules/zixflow-reactnative'

  # Your other dependencies...
end
Option B: FCM (For cross-platform Firebase apps) Update your ios/Podfile with static linkage:
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '13.0'

# Required for FCM
use_frameworks! :linkage => :static

target 'YourApp' do
  config = use_native_modules!

  use_react_native!(:path => config[:reactNativePath])

  # Add Zixflow with FCM support
  pod 'zixflow-reactnative/fcm', :path => '../node_modules/zixflow-reactnative'

  # Your other dependencies...
end
Install dependencies:
cd ios
pod install
cd ..
For modern Swift-based React Native apps with APNs: AppDelegate.swift:
import UIKit
import ZixflowMessagingPushAPN

@main
class AppDelegateWithZixflowIntegration: ZixflowAppDelegateWrapper<AppDelegate> {}

class AppDelegate: NSObject, UIApplicationDelegate {

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // Set notification delegate
    UNUserNotificationCenter.current().delegate = self

    // Initialize Zixflow push messaging (APNs)
    MessagingPushAPN.initialize(
      withConfig: MessagingPushConfigBuilder()
        .build()
    )

    return true
  }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
  // Show notifications when app is in foreground
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
  ) {
    completionHandler([.banner, .list, .badge, .sound])
  }
}
For FCM, import ZixflowMessagingPushFCM instead:
import ZixflowMessagingPushFCM

// Initialize with FCM
MessagingPushFCM.initialize(
  withConfig: MessagingPushConfigBuilder()
    .build()
)

Step 4: Configure AppDelegate (Objective-C)

If your React Native app uses Objective-C, create a Swift bridge: Create MyAppPushNotificationsHandler.swift:
import Foundation
import ZixflowMessagingPushAPN

@objc
public class MyAppPushNotificationsHandler: NSObject {

  @objc(setupZixflowClickHandling)
  public func setupZixflowClickHandling() {
    MessagingPushAPN.initialize(
      withConfig: MessagingPushConfigBuilder().build()
    )
  }

  @objc(application:deviceToken:)
  public func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
  ) {
    MessagingPush.shared.application(
      application,
      didRegisterForRemoteNotificationsWithDeviceToken: deviceToken
    )
  }

  @objc(application:error:)
  public func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
  ) {
    MessagingPush.shared.application(
      application,
      didFailToRegisterForRemoteNotificationsWithError: error
    )
  }
}
Update AppDelegate.mm:
#import "YourApp-Swift.h"

@implementation AppDelegate

MyAppPushNotificationsHandler* pnHandlerObj;

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // ... existing initialization code ...

  // Register for remote notifications
  [application registerForRemoteNotifications];

  // Initialize push handling
  pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
  [pnHandlerObj setupZixflowClickHandling];

  return YES;
}

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  [pnHandlerObj application:application deviceToken:deviceToken];
}

- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  [pnHandlerObj application:application error:error];
}

@end

Step 5: Rich Push with Notification Service Extension

To support rich push notifications (images, videos, GIFs), add a Notification Service Extension: 1. Create Notification Service Extension:
  1. In Xcode: FileNewTarget
  2. Select Notification Service Extension
  3. Name it NotificationServiceExtension
  4. Click Finish (activate scheme when prompted)
2. Add Zixflow to Extension: Update your ios/Podfile to include the extension:
# Main app target
target 'YourApp' do
  # ... existing configuration ...
  pod 'zixflow-reactnative/apn', :path => '../node_modules/zixflow-reactnative'
end

# Notification Service Extension target
target 'NotificationServiceExtension' do
  pod 'zixflow-reactnative-richpush/apn', :path => '../node_modules/zixflow-reactnative'
end
For FCM:
target 'NotificationServiceExtension' do
  pod 'zixflow-reactnative-richpush/fcm', :path => '../node_modules/zixflow-reactnative'
end
3. Install dependencies:
cd ios
pod install
cd ..
4. Update NotificationService.swift:
import UserNotifications
import ZixflowMessagingPushAPN

class NotificationService: UNNotificationServiceExtension {
  var contentHandler: ((UNNotificationContent) -> Void)?
  var bestAttemptContent: UNMutableNotificationContent?

  override func didReceive(
    _ request: UNNotificationRequest,
    withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
  ) {
    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

    // Initialize SDK for extension
    MessagingPushAPN.initializeForExtension(
      withConfig: MessagingPushConfigBuilder(apiKey: "YOUR_API_KEY")
                .build()
    )

    if let bestAttemptContent = bestAttemptContent {
      // Let Zixflow handle rich push and track delivery
      MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
    }
  }

  override func serviceExtensionTimeWillExpire() {
    // Called when extension is about to be terminated
    MessagingPush.shared.serviceExtensionTimeWillExpire()

    if let contentHandler = contentHandler,
       let bestAttemptContent = bestAttemptContent {
      contentHandler(bestAttemptContent)
    }
  }
}
Important: Replace "YOUR_API_KEY" with your actual API key.

Android Setup

Android push notifications use Firebase Cloud Messaging (FCM).

Step 1: Add Firebase to Your Project

  1. Go to Firebase Console
  2. Add your Android app to Firebase project
  3. Download google-services.json
  4. Place google-services.json in android/app/ directory

Step 2: Configure Gradle

Project-level android/build.gradle:
buildscript {
    ext {
        minSdkVersion = 24
        compileSdkVersion = 34
        targetSdkVersion = 34
    }

    dependencies {
        classpath 'com.google.gms:google-services:4.4.2'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}
App-level android/app/build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'  // Add this line

android {
    // ... existing configuration ...
}

dependencies {
    // React Native auto-linking handles Zixflow dependencies
    // ... your other dependencies ...
}

Step 3: Add Permissions

Add to android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Required for push notifications -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required for Android 13+ (API 33+) -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application>
        <!-- Your app configuration -->
    </application>
</manifest>

Step 4: Configure Notification Icon and Color (Optional)

Add to android/app/src/main/AndroidManifest.xml inside <application> tag:
<application>
    <!-- Default notification icon (white, transparent background) -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_icon"
        android:resource="@drawable/ic_notification" />

    <!-- Default notification color (accent color) -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_color"
        android:resource="@color/notification_color" />
</application>

React Native Integration

Step 1: Initialize SDK

Initialize the Zixflow SDK in your App.tsx:
import React, { useEffect } from 'react';
import { Zixflow, ZixflowConfig, ZixflowLogLevel } from 'zixflow-reactnative';

export default function App() {
  useEffect(() => {
    // Configure the SDK
    const config: ZixflowConfig = {
      apiKey: 'YOUR_API_KEY',
      logLevel: ZixflowLogLevel.debug,
      inApp: {
              },
      autoTrackDeviceAttributes: true,
      trackApplicationLifecycleEvents: true,
    };

    // Initialize the SDK
    Zixflow.initialize(config);

    console.log('Zixflow SDK initialized');
  }, []);

  return (
    // Your app UI
  );
}

Step 2: Request Push Permission

Request permission to send push notifications:
import React, { useState } from 'react';
import { Button, Alert } from 'react-native';
import { Zixflow, ZixflowPushPermissionStatus } from 'zixflow-reactnative';

export default function PushPermissionButton() {
  const [permissionStatus, setPermissionStatus] = useState<ZixflowPushPermissionStatus | null>(null);

  const requestPushPermission = async () => {
    try {
      const permission = await Zixflow.pushMessaging.showPromptForPushNotifications({
        ios: {
          sound: true,
          badge: true,
          alert: true,
        },
      });

      setPermissionStatus(permission);

      switch (permission) {
        case ZixflowPushPermissionStatus.Granted:
          Alert.alert('Success', 'Push notifications enabled!');
          console.log('Push notifications enabled');
          break;

        case ZixflowPushPermissionStatus.Denied:
          Alert.alert('Denied', 'Push notification permission was denied');
          console.log('Push notifications denied');
          break;

        case ZixflowPushPermissionStatus.NotDetermined:
          // iOS only: permission not yet asked
          console.log('Push notification permission not determined');
          break;
      }
    } catch (error) {
      console.error('Failed to request push permission:', error);
    }
  };

  return (
    <Button
      title="Enable Push Notifications"
      onPress={requestPushPermission}
    />
  );
}
Permission Options (iOS):
OptionTypeDescription
soundbooleanEnable notification sounds
badgebooleanEnable app badge updates
alertbooleanShow notification alerts

Step 3: Check Permission Status

Check current push permission status:
import { Zixflow, ZixflowPushPermissionStatus } from 'zixflow-reactnative';

const checkPushPermission = async () => {
  try {
    const status = await Zixflow.pushMessaging.getPushPermissionStatus();

    console.log('Push permission status:', status);

    if (status === ZixflowPushPermissionStatus.Granted) {
      console.log('Push notifications are enabled');
    } else if (status === ZixflowPushPermissionStatus.Denied) {
      console.log('Push notifications are disabled');
    } else {
      console.log('Push permission not determined');
    }
  } catch (error) {
    console.error('Failed to get push permission status:', error);
  }
};

Step 4: Identify Users

Critical: Device tokens must be associated with a user before push notifications can be delivered.
import { Zixflow } from 'zixflow-reactnative';

// Identify user when they log in
const handleLogin = async (userId: string, userEmail: string) => {
  try {
    // Identify user with Zixflow
    Zixflow.identify({
      userId: userId,
      traits: {
        email: userEmail,
        firstName: 'John',
        lastName: 'Doe',
        plan: 'premium',
      },
    });

    console.log('User identified:', userId);
  } catch (error) {
    console.error('Failed to identify user:', error);
  }
};
Important Notes:
  • When users start the app, they automatically generate a device token
  • This token links to their Zixflow profile when you call identify()
  • This ensures notifications are delivered to the correct user

Step 5: Get Device Token

Retrieve the registered device token:
import { Zixflow } from 'zixflow-reactnative';

const getDeviceToken = async () => {
  try {
    const token = await Zixflow.pushMessaging.getRegisteredDeviceToken();
    console.log('Device token:', token);
    return token;
  } catch (error) {
    console.error('Failed to get device token:', error);
    return null;
  }
};

Handle deep links from push notifications:

Setup Deep Linking

1. Configure Deep Links (iOS): Add to ios/YourApp/Info.plist:
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourapp</string>
    </array>
  </dict>
</array>
2. Configure Deep Links (Android): Add to android/app/src/main/AndroidManifest.xml:
<activity android:name=".MainActivity">
  <!-- Existing intent filter -->

  <!-- Deep link intent filter -->
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
      android:scheme="yourapp"
      android:host="*" />
  </intent-filter>

  <!-- Universal Links (optional) -->
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
      android:scheme="https"
      android:host="yourapp.com" />
  </intent-filter>
</activity>
Use React Navigation’s linking configuration:
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Linking } from 'react-native';

const Stack = createNativeStackNavigator();

const linking = {
  prefixes: ['yourapp://', 'https://yourapp.com'],
  config: {
    screens: {
      Home: 'home',
      Product: 'product/:id',
      Profile: 'profile',
    },
  },
};

export default function App() {
  return (
    <NavigationContainer linking={linking}>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Product" component={ProductScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Rich Push Notification Payloads

The Zixflow SDK automatically handles rich push notifications. Here’s how to structure payloads:

iOS APNs Payload

{
  "aps": {
    "mutable-content": 1,
    "alert": {
      "title": "New Product Available",
      "body": "Check out our latest premium features"
    },
    "badge": 1,
    "sound": "default"
  },
  "Zixflow": {
    "push": {
      "link": "yourapp://product/123",
      "image": "https://example.com/product-image.jpg"
    }
  }
}

iOS FCM Payload

{
  "message": {
    "apns": {
      "payload": {
        "aps": {
          "mutable-content": 1,
          "alert": {
            "title": "Special Offer",
            "body": "Limited time discount!"
          }
        },
        "Zixflow": {
          "push": {
            "link": "https://yourapp.com/offers",
            "image": "https://example.com/offer.png"
          }
        }
      },
      "headers": {
        "apns-priority": 10
      }
    }
  }
}

Android FCM Payload

{
  "message": {
    "data": {
      "title": "Order Shipped",
      "body": "Your order is on the way!",
      "image": "https://example.com/shipping.jpg",
      "link": "yourapp://orders/456"
    }
  }
}

Testing Push Notifications

1. Verify Device Token Registration

Check if device token is registered:
const verifyPushSetup = async () => {
  try {
    const token = await Zixflow.pushMessaging.getRegisteredDeviceToken();
    const status = await Zixflow.pushMessaging.getPushPermissionStatus();

    console.log('Device token:', token);
    console.log('Permission status:', status);

    if (token && status === ZixflowPushPermissionStatus.Granted) {
      console.log('✓ Push notifications ready');
    } else {
      console.log('✗ Push setup incomplete');
    }
  } catch (error) {
    console.error('Push verification failed:', error);
  }
};

2. Enable Debug Logging

Set log level to debug during development:
const config: ZixflowConfig = {
  apiKey: 'YOUR_API_KEY',
  logLevel: ZixflowLogLevel.debug,  // Verbose logging
};

Zixflow.initialize(config);

3. Test on Physical Devices

iOS:
  • Push notifications do not work on iOS Simulator
  • Must use physical iOS device
  • Ensure APNs certificate is configured in Zixflow dashboard
Android:
  • Emulator works if Google Play Services installed
  • Physical device always recommended
  • Ensure FCM Server Key is configured in Zixflow dashboard

4. Send Test Push from Zixflow

  1. Log into Zixflow dashboard
  2. Navigate to MessagingPush Notifications
  3. Create test push notification
  4. Send to specific user (using userId from identify())

Troubleshooting

iOS Push Not Received

Symptoms: Push sent but not delivered on iOS Solutions:
  1. Verify Push Notifications capability enabled in Xcode
  2. Check APNs certificate uploaded to Zixflow dashboard
  3. Ensure testing on physical device (not simulator)
  4. Verify user identified: Zixflow.identify({ userId: ... })
  5. Check permission granted: getPushPermissionStatus()
  6. Review logs for errors

Android Push Not Received

Symptoms: Push sent but not delivered on Android Solutions:
  1. Verify google-services.json in android/app/ directory
  2. Check FCM Server Key in Zixflow dashboard
  3. Ensure POST_NOTIFICATIONS permission granted (Android 13+)
  4. Verify Google Services plugin applied
  5. Check device token registered
  6. Review logs for errors

Rich Push Not Working (Images)

Symptoms: Images not loading in notifications iOS Solutions:
  1. Ensure Notification Service Extension properly configured
  2. Verify extension has correct dependencies in Podfile
  3. Check App Groups configuration (if using)
  4. Verify image URL is accessible
  5. Check extension logs for errors
Android Solutions:
  1. Verify image URL is accessible from device
  2. Check internet connectivity
  3. Ensure image format is supported (JPEG, PNG, GIF)

Permission Denied

Symptoms: User denied push permission Solutions:
  1. Explain value before requesting permission
  2. Guide users to Settings to enable manually
  3. Show in-app message explaining benefits
  4. Request permission at appropriate time (after user engagement)

Device Token Not Registered

Symptoms: getRegisteredDeviceToken() returns empty or fails Solutions:
  1. Verify SDK initialized before calling
  2. Check permission granted
  3. Ensure identify() called to associate token
  4. Review native logs for errors
  5. Rebuild app and retry

Best Practices

  1. Request permission thoughtfully - Explain value before asking
  2. Identify users immediately after authentication
  3. Test on physical devices - Simulators have limitations
  4. Use rich push - Images increase engagement
  5. Handle deep links - Provide seamless navigation
  6. Monitor metrics - Track delivery and open rates
  7. Enable debug logging - During development only
  8. Test both platforms - iOS and Android behave differently
  9. Clear identity on logout - Call clearIdentify()
  10. Keep credentials secure - Never commit API keys to git