Setting Up Push Notifications for iOS Apps with Firebase Cloud Messaging (FCM) and Google Cloud Platform (GCP)

3 minute read

Overview

This guide walks through the complete setup for sending push notifications to an iOS app using Firebase Cloud Messaging (FCM), Google Cloud Functions, and other GCP services. We’ll cover the iOS app configuration, backend setup, and architecture.

Architecture Diagram

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│                 │     │                 │     │                 │
│  iOS App        │◄────┤  Firebase       │◄────┤  Google Cloud   │
│  (SwiftUI)      │     │  Cloud          │     │  Functions      │
│                 │     │  Messaging      │     │                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        ▲                        ▲                        ▲
        │                        │                        │
        │                        │                        │
        │                        │                        │
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│                 │     │                 │     │                 │
│  Apple Push     │     │  Google Cloud   │     │  Cloud          │
│  Notification   │     │  Storage        │     │  Scheduler      │
│  Service (APNs) │     │  (Firestore)    │     │                 │
│                 │     │                 │     │                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘

1. iOS App Setup

Prerequisites

  • Xcode 14+
  • iOS 15+
  • Apple Developer Account
  • Firebase Account

Step 1: Create iOS Project

// AwayAppsApp.swift
import SwiftUI
import FirebaseCore
import FirebaseMessaging

@main
struct AwayAppsApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Step 2: Configure AppDelegate

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        UNUserNotificationCenter.current().delegate = self
        Messaging.messaging().delegate = self
        
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            if granted {
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        return true
    }
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Messaging.messaging().apnsToken = deviceToken
    }
    
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("FCM registration token: \(fcmToken ?? "")")
        // Send token to your backend
    }
}

Step 3: Add Required Files

  • Add GoogleService-Info.plist to your project
  • Configure entitlements for push notifications
  • Add required capabilities in Xcode

2. Firebase Setup

Step 1: Create Firebase Project

  1. Go to Firebase Console
  2. Create new project
  3. Add iOS app to project
  4. Download GoogleService-Info.plist

Step 2: Configure APNs

  1. In Firebase Console, go to Project Settings
  2. Add APNs authentication key from Apple Developer Portal
  3. Upload the key to Firebase

3. Google Cloud Functions Setup

Step 1: Create Cloud Function

// index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.sendNotification = functions.https.onRequest(async (req, res) => {
    try {
        const { token, title, body } = req.body;
        
        const message = {
            notification: {
                title,
                body
            },
            token
        };
        
        const response = await admin.messaging().send(message);
        res.status(200).send(response);
    } catch (error) {
        res.status(500).send(error);
    }
});

Step 2: Deploy Function

firebase deploy --only functions

4. Cloud Scheduler Setup

Step 1: Create Scheduled Job

  1. Go to Cloud Scheduler in GCP Console
  2. Create new job
  3. Set schedule (e.g., 0 8 * * * for 8 AM daily)
  4. Configure HTTP target to call your Cloud Function

5. Testing

Step 1: Get FCM Token

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    print("FCM registration token: \(fcmToken ?? "")")
}

Step 2: Send Test Notification

  1. Use Firebase Console
  2. Send test message using FCM token
  3. Verify receipt on device

Common Issues and Solutions

  1. “Default app has already been configured”
    • Ensure FirebaseApp.configure() is called only once
    • Remove from SwiftUI init if present in AppDelegate
  2. Missing Info.plist
    • Set “Generate Info.plist File” to Yes in Build Settings
    • Add custom keys via Info tab
  3. Push Notifications Not Working in TestFlight
    • Ensure aps-environment is set to production
    • Verify APNs key is uploaded to Firebase
    • Check device token is current

Best Practices

  1. Token Management
    • Store FCM tokens in your backend
    • Update tokens when they refresh
    • Remove invalid tokens
  2. Error Handling
    • Implement proper error handling in Cloud Functions
    • Log failed notifications
    • Implement retry logic
  3. Testing
    • Test on real devices
    • Test in different app states (foreground, background, terminated)
    • Test with different notification types

Conclusion

This setup provides a robust foundation for sending push notifications to iOS apps. The architecture is scalable and can be extended to support additional features like notification grouping, rich notifications, and analytics.