Seamless Notifications Across Different Platforms
I will show you how to set up Firebase push notifications for Android and iOS in an Ionic 8 and Angular 18 app using the Ionic Push Notifications Capacitor Plugin API, including Firebase configuration for multiple environments.
Key Steps:
Firebase Configuration for Multiple Environments: You'll configure Firebase for development, QA, and production environments, ensuring that notifications work seamlessly across all stages of your application.
Ionic Capacitor Plugin for Push Notifications: The Ionic Push Notifications Capacitor Plugin API will be used for handling push notifications on both Android and iOS platforms.
Android/iOS Adjustments: Specific configurations are required for each platform to ensure notifications are received properly.
Node.js Backend Integration with FCM: A Node.js backend will be used to send push notifications, using Firebase Cloud Messaging to deliver notifications to users across different environments.
Step 1: Firebase Configuration Files for multiple Environment Support
When you integrate Firebase into an app, Firebase provides you with two configuration files:
google-services.json
for Android.GoogleService-Info.plist
for iOS.
In a multi-environment scenario (e.g., Dev, QA, and Prod), Firebase provides separate configuration files for each project. To manage this efficiently, I organized these files in dedicated folders within the Android and iOS directories.
Folder Structure
For Android:
android/
├── app/
│ ├── google-services.json (placeholder)
├── beta/
│ ├── google-services.json (QA)
├── debug/
│ ├── google-services.json (Development)
├── release/
├── google-services.json (Production)
For iOS:
ios/
├── App/
│ ├── App/
│ ├── GoogleService-Info.plist (placeholder)
├── beta/
│ ├── GoogleService-Info.plist (QA)
├── debug/
│ ├── GoogleService-Info.plist (Development)
├── release/
├── GoogleService-Info.plist (Production)
Each folder contains a different configuration file that corresponds to its environment.
Step 2: Customizing angular.json
for Multi-Environment Builds
To make the build process easier for different environments, I customized the angular.json
file. This lets Angular use the right environment settings based on whether you're building for development, QA, or production.
"configurations": {
"dev": {
"sourceMap": true,
"namedChunks": true,
"vendorChunk": true,
"optimization": false,
"buildOptimizer": false,
"extractLicenses": false
},
"qa": {
"aot": true,
"sourceMap": false,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.qa.ts"
}
]
},
"prod": {
"aot": true,
"sourceMap": false,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
This setup helps optimize your build configurations based on the environment. For example, dev
configurations focus on faster builds, while prod
ensures your build is highly optimized for performance.
Step 3: Build Commands for iOS and Android
To make switching between different Firebase projects easier during builds, I created custom commands in package.json
. These commands automate copying the correct Firebase configuration file and syncing with Capacitor.
Here’s what I added to the package.json
:
"scripts": {
"build:ios:qa": "ionic build --qa && cp ios/App/App/beta/GoogleService-Info.plist ios/App/App/GoogleService-Info.plist && ionic cap sync ios",
"build:ios:dev": "ionic build --dev && cp ios/App/App/debug/GoogleService-Info.plist ios/App/App/GoogleService-Info.plist && ionic cap sync ios",
"build:android:qa": "ionic build --qa && cp android/app/src/beta/google-services.json android/app/google-services.json && ionic cap sync android",
"build:android:dev": "ionic build --dev && cp android/app/src/debug/google-services.json android/app/google-services.json && ionic cap sync android",
"build:ios:prod": "ionic build --prod && cp ios/App/App/release/GoogleService-Info.plist ios/App/App/GoogleService-Info.plist && ionic cap sync ios",
"build:android:prod": "ionic build --prod && cp android/app/src/release/google-services.json android/app/google-services.json && ionic cap sync android"
}
Explanation of the Commands:
build:ios:qa
: Builds the app for QA, copies the correctGoogleService-Info.plist
from thebeta
folder, and syncs the changes to iOS using Capacitor.build:android:prod
: Builds the app for production, copies the correctgoogle-services.json
from therelease
folder, and syncs with Android.
This setup makes switching between environments seamless, saving you from manually replacing files each time you switch builds.
Android Setup
build.gradle
(Android)
For Firebase push notifications on Android, the Firebase services plugin should be applied conditionally if google-services.json
is present. This ensures the build won't fail when the file is missing, which is useful in multi-environment setups.
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
This block ensures that Firebase services are applied only when google-services.json
exists.
Android Manifest (AndroidManifest.xml
)
In the AndroidManifest file, you need to configure a default notification icon and set internet permissions.
<application>
<meta-data
android:resource="@mipmap/ic_launcher"
android:name="com.google.firebase.messaging.default_notification_icon" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
The meta-data
entry ensures a default icon is used for Firebase notifications, and the INTERNET
permission is essential for Firebase messaging.
iOS Setup
Podfile
Configuration
For iOS, Firebase Messaging needs to be added to the Podfile
in order to enable push notifications.
target 'App' do
capacitor_pods
pod 'Firebase/Messaging'
end
Run pod install
after modifying the Podfile to apply these changes.
AppDelegate.swift
Modifications
Update the AppDelegate.swift
file to set up Firebase and manage push notification registration.
import Firebase
import FirebaseMessaging
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
Messaging.messaging().token(completion: { (token, error) in
if let error = error {
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
} else if let token = token {
NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: token)
}
})
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}
These methods handle push notification registration and token retrieval for Firebase Messaging on iOS.
Frontend: Capacitor Push Notifications in Ionic
To register the device for push notifications and manage the token, use Capacitor’s PushNotifications
API.
Registering for Notifications
In the frontend, you need to request permissions and register the device for push notifications. The device token is retrieved upon successful registration.
import { PushNotifications } from "@capacitor/push-notifications";
private RegisterNotifications = async () => {
let permission = await PushNotifications.checkPermissions();
if (permission.receive === "prompt") {
permission = await PushNotifications.requestPermissions();
}
if (permission.receive !== "granted") {
throw new Error("User denied permissions!");
}
await PushNotifications.register();
};
Listening for the Device Token
Once registered, listen for the token used to send notifications to this device:
private NotificationListeners = async () => {
await PushNotifications.addListener("registration", (token) => {
console.info("Registration token: ", token.value);
});
};
On iOS, the token contains the APNS token.
On Android, the token contains the FCM token.
Backend: Sending Push Notifications
In the backend, I use Node.js with Firebase Cloud Functions to send push notifications. The FCM messaging.send()
method is used to send notifications to registered devices.
Once you have the device token (retrieved from the frontend), you can pass it to this function to send a push notification to the targeted device.
Conclusion
With this setup, Firebase push notifications are integrated across both Android and iOS environments in your Ionic and Angular app. The project is organized to support multi-environment builds (Dev, QA, Prod), and the notification handling is managed in the backend using Firebase Cloud Functions.
Here’s a summary of what we've covered:
Android Setup: Changes in
build.gradle
,AndroidManifest.xml
, and Firebase configuration files for each environment.iOS Setup: Firebase Messaging in the
Podfile
and push notification handling inAppDelegate.swift
.Frontend: Push notifications registration and token retrieval using Capacitor’s
PushNotifications
API.Backend: Sending notifications using Firebase Cloud Functions in Node.js.
With these steps, you should now have a fully functional Firebase push notification system for your mobile app.
Subscribe to my newsletter
Read articles from Ariyan Ashfaque directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by