The Complete Guide to Capacitor Push Notifications: iOS, Android & Firebase

Prerequisites: read this before you start

Unlike Android development, where you can test almost everything for free, Apple puts Push Notifications behind a paywall. Before trying to follow this guide, ensure you meet these requirements:

The “Entry Fee”

Apple: You MUST have an active Apple Developer Program membership ($99/year). You cannot implement Push Notifications with a free Apple ID. The “Push Notifications” capability will simply not appear in your provisioning profiles.

Android: Totally free to develop. You only pay the $25 (one-time fee) when you are ready to upload the app to the Google Play Store.

Required Roles

To create the necessary Certificates, Identifiers, and Keys (.p8), you need the right permissions in your team:

Apple Developer Portal: You need to be the Account Holder or have the Admin role. (Sometimes the “App Manager” role works, but “Developer” accounts often have restrictions on creating new Identifiers or Keys).

Firebase Console: You need Editor or Owner permissions to upload the keys and change project settings.

System Requirements:

Mac User: You need a Mac with Xcode installed to configure the iOS capabilities properly.

Physical iPhone: As mentioned later, you cannot test APNs on a Simulator.

Introduction

Stop struggling with APNs tokens and UNIMPLEMENTED errors. Here is the definitive guide to integrating @capacitor-firebase/messaging correctly, covering the basics you might have missed.

The Problem: When the Standard Plugin Just Isn’t Enough

If you have ever tried to implement Push Notifications in Capacitor following the official documentation, you might have hit the “iOS Wall.”

On Android, everything usually works fine. But on iOS, the standard plugin returns a native APNs Token (hexadecimal). When you try to use this token in the Firebase Console… silence. This is because Firebase Cloud Messaging (FCM) expects an FCM Token, not Apple’s native one.

The robust solution in 2026 is to use the community-maintained plugin: @capacitor-firebase/messaging. It handles the native swizzling (intercepting the token) and gives you a unified FCM token for both platforms.

In this guide, I will walk you through the mandatory basics and the friction points that usually break production builds.

Step 1: Firebase Project Setup

Before touching the code, we need to tell Google and Apple who we are.

Android Setup

  1. Go to Firebase Console > Project Settings.
  2. Add an Android App using your Package Name (e.g., com.yourcompany.app).
  3. Download google-services.json and place it inside android/app/.
  4. Check your Gradle files: Ensure com.google.gms.google-services is included in your build.gradle dependencies.

iOS Setup (The Tricky Part)

  1. Add an iOS App in Firebase Console using your Bundle ID.
  2. Download GoogleService-Info.plist.
  3. Crucial: Add this file to your project using Xcode (drag and drop into App/App).
  4. The “Silent” Error Fix: Click on the file in Xcode. In the right panel, ensure “Target Membership” is checked for “App”.
  5. Double Check: Go to Build Phases > Copy Bundle Resources. Ensure the plist file is listed there. If it’s missing, the app will crash or fail silently at runtime.

Step 2: The “Boring” But Mandatory Apple Config

Enable Capabilities in Xcode

  1. Open your project: npx cap open ios.
  2. Select App (Root) > Target App > Signing & Capabilities.
  3. Add Push Notifications.
  4. Add Background Modes and check Remote notifications.

Provisioning Profiles (Regenerate!)
If you added the “Push” capability to your App ID after generating your provisioning profiles, they are invalid. You must go to the Apple Developer Portal, regenerate the profiles, and download them again in Xcode (or let Xcode manage signing automatically).

One Key to Rule Them All (.p8)

  1. Use an APNs Authentication Key (.p8) instead of old certificates.
  2. Go to Apple Developer Portal > Keys. Create a key with APNs enabled.
  3. Download the .p8 file (Save it!).
  4. Upload it to Firebase Console > Project Settings > Cloud Messaging.

Tip: Ensure you upload it to the exact Firebase project linked to your app. Mixing up Team IDs or Key IDs will stop messages dead in their tracks.

Step 3: Installation & Migration

Let’s switch engines.

** 1. Remove legacy plugin (if installed)**
npm uninstall @capacitor/push-notifications
rm -rf ios/App/Pods 
rm ios/App/Podfile.lock

** 2. Install Community Plugin**
npm install @capacitor-firebase/messaging firebase
npx cap sync

Step 4: Critical iOS Configuration

1. Update the Podfile (iOS 15.0+)
Open ios/App/Podfile. Bump the platform version to at least 15.0.

# ios/App/Podfile
platform :ios, '15.0' # <--- CRITICAL
use_frameworks! 
# Note: If 'use_frameworks!' causes conflicts with other plugins, 
# try 'use_modular_headers!' instead.

target 'App' do
  capacitor_pods
  pod 'Firebase/Messaging'
end

2. Simplify the AppDelegate
We need to initialize Firebase. The plugin handles the rest via Swizzling (make sure FirebaseAppDelegateProxyEnabled is NOT set to NO in your Info.plist, or you’ll have to handle methods manually).

Open ios/App/App/AppDelegate.swift:

import UIKit
import Capacitor
import FirebaseCore
import FirebaseMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, 
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

Step 5: Android 13+ Permissions

Open android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

Step 6: The Code (React/TypeScript)

import { FirebaseMessaging } from '@capacitor-firebase/messaging';

const setupNotifications = async () => {
  try {
    // 1. Request permission
    const { receive } = await FirebaseMessaging.requestPermissions();

    if (receive === 'granted') {
      // 2. Get the FCM Token (Unified for iOS & Android!)
      const { token } = await FirebaseMessaging.getToken();
      console.log('My FCM Token:', token);
    } else {
      console.warn('Permission denied');
    }

    // 3. Listen for notifications
    await FirebaseMessaging.addListener('notificationReceived', (event) => {
      console.log('Notification:', event);
    });

  } catch (error) {
    console.error('Error:', error);
  }
};

The “iOS Wall”: Pre-Flight Checklist

If one of these is red, it won’t work.

  • Are you using a Simulator? Push Notifications DO NOT work on iOS Simulators for APNs. You must use a physical iPhone.
  • App ID Capabilities: Does your App ID in the Apple Portal have “Push Notifications” enabled?
  • Provisioning Profile: Did you regenerate the profile after enabling Push capabilities?
  • Build Phases: Is GoogleService-Info.plist in “Copy Bundle Resources”?
  • Firebase Project: Did you upload the .p8 key to the correct Firebase project?

Conclusion

Migrating to @capacitor-firebase/messaging provides a production-ready architecture. You get unified FCM tokens, rich notifications support, and you stop fighting with APNs quirks.

Leave a Reply