Copied RSS Feed

ASP.NET

Firebase Push Notifications for Android and iOS Using Ionic and ASP.NET⁠-A Complete Guide

In this blog, we are going to learn how to build an Ionic Angular app for implementing Firebase push notifications for both Android and iOS. We’ll create an ASP.NET project with SQL to send Firebase push notifications.

Prerequisites

To check whether Node.js is already installed in your machine, ask for its version using the following command. If it is already installed, it returns the version number.

node -v
v14.18.0

Otherwise, it returns the following error.
To check whether the npm is already installed, execute the following command to ask for its version. This command returns the version if npm is already installed.

npm -v
6.14.15

Otherwise, it returns the following error.
To check whether the Angular CLI is already installed, execute the following command to ask its version.

ng version Angular CLI : 13.2.3

To check whether the Ionic CLI is already installed in your machine, execute the following command to ask for its version. If it is already installed, it returns the version details.

ionic info

Otherwise, it returns the following error.
Install the missing components using the respective commands in the following table.

Package Command
Node.js Node.js
npm Installing Node.js will install npm.
Angular CLI npm i @angular/cli
Ionic npm install -g ionic

Create an Ionic Angular application

After ensuring that the Angular CLI and Ionic are installed, create a new Ionic-with-Angular app using the following command. I am naming my app PushNotificationApp.

ionic start PushNotificationApp

Now, the terminal will request us to choose the framework. Make sure to choose Angular. Refer to the following image.
Then, the terminal will request us to choose the starter template, like in the following image. Make sure to choose blank.

Install Capacitor

Let’s install Capacitor in our app using the following command.

>  ionic integrations enable capacitor

Once the app has been created successfully, switch to the newly created project directory using the following command.

>  cd .\PushNotificationApp

Run the npx cap init command. It will allow us to fill out our app’s information like the app name and Package ID to initialize Capacitor configuration.

npx cap init
> App name: PushNotificationApp
> App Package ID: com.pushnotification.app

Refer to the following screenshot.

Build app

Before adding any native platforms to this project, the app must be built at least once. A web build creates the web assets directory that Capacitor needs (www folder in Ionic Angular projects). Use the following command to build the project.

>  Ionic build

Add platforms

Let’s add the iOS and Android platforms to this app in this section.
Before adding the Android platform, make sure to install the capacitor/android package using the following command.

>  npm install @capacitor/android

Now, add the Android platform using the next command.

>  npx cap add android

By running these commands, Android folders are created at the root of the project.
Before adding iOS platform, make sure to install the capacitor/ios package using the following command.

>  npm install @capacitor/ios

Now, add the iOS platform with the next command.

>  npx cap add ios

Running these commands creates iOS folders at the root of the project.

Create service

Create common services to invoke Web API calls and the HTTPClient inside the Service folder using the following command.
Let’s point to the projects src/app folder and run the following commands.

PushNotificationApp /src/app> ionic generate service Service/WebApi

PushNotificationApp /src/app> ionic generate service Service/HttpProvider

Add the following code in the web-api.service.ts file in the following locations.
src/app/Service/web-api.service.ts

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
import { catchError } from 'rxjs/internal/operators/catchError';
import { HttpHeaders, HttpClient } from '@angular/common/http';

@Injectable({
    providedIn: 'root'
})
export class WebApiService {
    constructor(private httpClient: HttpClient) { }

    // Post call method 
    // Param 1 : url
    // Param 2 : model
    post(url: string, model: any): Observable<any> {
      const httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json'
        }), 
       observe: "response" as 'body'
      };
      return this.httpClient.post(
        url,
        model,
        httpOptions)
        .pipe(
          map((response: any) => this.ReturnResponseData(response)),
          catchError(this.handleError)
        );
    }
    private ReturnResponseData(response: any) {
      return response;
    }
    private handleError(error: any) {
      return throwError(error);
    }
}

src/app/Service/http-provider.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { WebApiService } from './web-api.service';

var apiUrl = "https://localhost:44370/";

var httpLink = {
    addPushNotification: apiUrl + "api/pushNotification/addPushNotification",
    sendPushNotification: apiUrl + "api/pushNotification/sendPushNotification"
}
@Injectable({
    providedIn: 'root'
})
export class HttpProviderService {
    constructor(private webApiService: WebApiService) { }

    public addPushNotification(pushNotificationsDetail: any): Observable<any> {
      return this.webApiService.post(httpLink.addPushNotification , pushNotificationsDetail);
   }
    public sendPushNotification(pushNotificationsDetail: any): Observable<any> {
      return this.webApiService.post(httpLink.sendPushNotification,pushNotificationsDetail);
    }
}

Integration of Push Notification API

First, install the following push notification plugin to enable push notifications.

> npm install @capacitor/push-notifications

In the app.component.ts file, add the import section and const to make use of the push notification API.

const { PushNotifications } = Plugins;

import {
Plugins,
PushNotification,
PushNotificationToken,
PushNotificationActionPerformed
} from ‘@capacitor/core’;

Then, add the push notifications API for registration, registrationError, pushNotificationReceived, and pushNotificationActionPerformed inside the ngOnInit() function in app.component.ts to register and monitor push notifications.
Refer to the following code example.

PushNotifications.requestPermission().then(result => {
      if (result.granted) {
        PushNotifications.register();
      } else {
      }
    });

    PushNotifications.addListener('registration', (token: PushNotificationToken) => {
      let registrationToken = token.value;      
    });

    PushNotifications.addListener('registrationError', (error: any) => {
    });

    PushNotifications.addListener('pushNotificationReceived', (notification: PushNotification) => {
    });

    PushNotifications.addListener('pushNotificationActionPerformed', (args: PushNotificationActionPerformed) => { 
    }    );

registration: Used to register the push notification token of the app during installation.
registrationError: Used to handle any error occurring during the registration.
pushNotificationReceived: Used to handle notifications received when the app screen is active.
pushNotificationActionPerformed: Used to handle the action to be performed when interacting with a push notification.
I’ve shared the complete code of app.component.ts for your reference.

import { Component } from '@angular/core';
import {
  Plugins,
  PushNotification,
  PushNotificationToken,
  PushNotificationActionPerformed
} from '@capacitor/core';
import { AlertController, Platform } from '@ionic/angular';
import { HttpProviderService } from './Service/http-provider.service';
const { PushNotifications } = Plugins;

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss'],
})
export class AppComponent {

  notificationDetail = {
    title: '',
    body: ''
  };

  constructor(private alertCtrl: AlertController, private httpProvider: HttpProviderService, private platform: Platform) { }

  ngOnInit() {
    PushNotifications.requestPermission().then(result => {
      if (result.granted) {
        PushNotifications.register();
      } else {
      }
    });
    PushNotifications.addListener('registration', (token: PushNotificationToken) => {
      let registrationToken = token.value;
      if (registrationToken != null && registrationToken != 'null') {
        localStorage.setItem("registrationToken", registrationToken);
        var DeviceType = 0;
        if (this.platform.is("android")) {
          DeviceType = 1;
        }
        else if (this.platform.is("ios")) {
          DeviceType = 2;
        }

        var pushNotificationsDetail = {
          DeviceId: registrationToken,
          DeviceType: DeviceType
        }

        this.httpProvider.addPushNotification(pushNotificationsDetail).subscribe(async data => {
          if (data != null && data.body != null && data.body.isSuccess == true) {
            localStorage.setItem("IsSuccessPushRegistration", "true");
            this.notificationDetail.title = "Success";
            this.notificationDetail.body = data.body.message;
            this.showPushNotificationAlert(this.notificationDetail);
          }
        },async error => {});
      }
    },
    );

    PushNotifications.addListener('registrationError', (error: any) => {
    });

    PushNotifications.addListener('pushNotificationReceived', (notification: PushNotification) => {
      this.showPushNotificationAlert(notification);
    });

    PushNotifications.addListener('pushNotificationActionPerformed', (args: PushNotificationActionPerformed) => {
    });
  }

  async showPushNotificationAlert(notificationDetail: any) {
    const alert = await this.alertCtrl.create({
      header: notificationDetail.title,
      message: notificationDetail.body,
      buttons: ['OK']
    });
    await alert.present();
  }
}

When token registration of the app is completed, store the registration token in the database for future use.
When a push notification is received when the app is open, the notification will show in a pop-up message.

Create page

In this section, we will send a message through a push notification using the Web API server.
Add all the basic validations like Title and Body in the HTML file. Refer to the following code.
src/app/home.component.ts

import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { AlertController, Platform } from '@ionic/angular';
import { HttpProviderService } from '../Service/http-provider.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  notificationList = {
    NotificationTitle: '',
    NotificationBody:''
  };

  notificationDetail = {
    title: '',
    body: ''
  };

  @ViewChild('sendForm', { static: false }) sendForm: NgForm;

  constructor(private router: Router, private route: ActivatedRoute, public alertController: AlertController,
    private httpProvider: HttpProviderService, private platform : Platform) {
    this.route.queryParams.subscribe(params => {
      if (this.router.getCurrentNavigation() && this.router.getCurrentNavigation().extras &&
        this.router.getCurrentNavigation().extras.state) {
        let data = this.router.getCurrentNavigation().extras.state;
        this.showPushNotificationAlert(data);
      }
    });
  }

  async showPushNotificationAlert(notificationDetail: any) {
    const alert = await this.alertController.create({
      header: notificationDetail.title,
      message: notificationDetail.body,
      buttons: ['OK']
    });
    await alert.present();
  }

  sendMessage(form: NgForm) {
    if (form.valid) {
      this.httpProvider.sendPushNotification(this.notificationList).subscribe(async data => {
        if (data != null && data.body != null && data.body.isSuccess == true) {
          this.notificationDetail.title = "Success";          
          this.notificationDetail.body = data.body.message;
          this.showPushNotificationAlert(this.notificationDetail);
        }
      },async error => {});
    }
    else {
      this.notificationDetail.title = "Warning";
      this.notificationDetail.body = "Please fill the message";
      this.showPushNotificationAlert(this.notificationDetail);
    }
  }
}

src/app/home.component.html

<ion-content>
    <form #sendForm="ngForm" novalidate>
        <ion-row class="row-style">
            <ion-col class="align-center p-30">
                <div class="label-message">
                    Title
                </div>
            </ion-col>
        </ion-row>
        <ion-row class="row-style">
            <ion-col class="align-center p-30">
                <ion-input type="text" id="message" name="message" #message="ngModel"
                    [(ngModel)]="notificationList.NotificationTitle" placeholder="Type your title" required></ion-input>
            </ion-col>
        </ion-row>
        <ion-row class="row-style">
            <ion-col class="align-center p-30">
                <div class="label-message">
                    Description
                </div>
            </ion-col>
        </ion-row>
        <ion-row class="row-style">
            <ion-col class="align-center p-30">
                <ion-textarea type="text" id="message" name="message" #message="ngModel"
                    [(ngModel)]="notificationList.NotificationBody" placeholder="Type your description" maxlength="120"
                    required></ion-textarea>
            </ion-col>
        </ion-row>
        <ion-row class="row-style">
            <ion-col class="align-center submit-btn p-30">
                <ion-button type="submit" (click)="sendMessage(sendForm)">Send Message</ion-button>
            </ion-col>
        </ion-row>
    </form>
</ion-content>

The following are screenshots of the Send Notification page.

Now, run the following commands to update code changes in the Android and iOS app builds.

> ionic build
> npx cap copy
> npx cap sync

Creating a project for your app on Firebase

Go to the Firebase Console, sign in with your email ID, and click the Create a project button, as shown in the screenshot.
Add a name for the project. I am naming mine PushNotificationApp. Accept the Firebase ToS and click Continue. A Project ID will be automatically generated.

Then, click Continue.

Select the default account for Firebase and click Create Project.
Your project is ready to use. Now, click Continue.

Android

Integrating Firebase with the Android app

Go to the Project Overview page in the Firebase project and at the top, click the Android icon to add a new Android app.
The next screen will request some information about your application:

  • The Android package name you provide here should match the app ID from your capacitor.config.json file. We used the name com.pushnotification.app for this Capacitor app ID, so that is what we will use for this entry.
  • Nickname and Debug Signing Certificate are optional.

Then, click Register app.

Download and use the google-services.json file

The next prompt will ask you to download a google-services.json file. This file contains the information your Capacitor app needs to connect to Firebase from Android.
Download the google-services.json file to your local machine. Then, move the file into your Capacitor Android project directory, under android/app/.

iOS

Prerequisites

iOS push notifications are more complicated than Android. Prior to being able to test push notifications with your iOS app, you must have a paid Apple Developer account to take care of the following items:

  1. Set up the proper development or production certificates and provisioning profiles for your iOS app in the Apple Developer Portal.
  2. Create an APNS certificate or key for either development or production in the Apple Developer Portal.
Integrating Firebase with our native iOS app

Go to the Project Overview page for the Firebase project and at the top, click the iOS icon to add a new iOS app.

The next screen will ask you for some information about your app:

  • The iOS bundle ID should match the app ID from your capacitor.config.jsonfile. We used the name com.pushnotification.app for this Capacitor app ID, so that is what we will use for this entry.
  • App Nickname and App Store ID are optional.

Then, click Register app.

Add the GoogleService-Info.plist file to your iOS app

Download the GoogleService-Info.plist provided to your local machine.
Next, open the project in Xcode and run the following command.

npx cap open ios

Enable push notifications in Xcode.
Check the listed items inside the App > Signing & Capabilities tab.
Under the Signing section, click + Capability. Then, search for push notifications in the current modal.

Add push notifications like in the following screenshot.

Add the GoogleService-Info.plist in Xcode.

Open Podfile in Xcode.
Add pod FirebaseCore and pod Firebase/Messaging to your target App section. Refer to the following code example.

target 'App' do
capacitor_pods
# Add your Pods here
pod 'FirebaseCore' # Add this line
pod 'Firebase/Messaging' # Add this line
end

Run npx cap update ios to update the Ionic project.

Add initialization code

To connect to Firebase when your iOS app starts up, follow these steps:

  1. Open AppDelegate.swift in Xcode and import Firebase at the top of the file.
    import Firebase
  1. Then, add the configuration method of Firebase to the following initialization code in the application (didFinishLaunchingWithOptions) method.
    FirebaseApp.configure()

    Note: On iOS, you will see a pop-up asking you to allow notifications for your app—make sure you choose to allow notifications!

  1. Search for the keyword Push inside App > Build Setting Tab, as shown in the screenshot. Then ensure that the following items are added.

Generating .p8 key file

To achieve APNS, we are using the jose-jwt NuGet package for iOS devices from the server-side.
Jose JWT will work only for .NET Framework 4.6.1 and later versions.
Here, we are using the .p8 file to get a private key and send notifications.
To generate a .p8 key file, go to your Apple developer account page and select Certificates, IDs & Profiles.
Then, select Keys.
Click the + button to add a new key.
On the new key page, type in your key name and check the Apple Push Notification service (APNs) box. Then, click Continue and click Register.
Click Download.
The authentication key filename will look like this: AuthKeyABCD1234.p8. The ABCD1234 is the Key ID.

Upload the APNS certificate or key to Firebase

You can now upload the .p8 file (APNS auth key) to your Firebase console.
To upload your auth key, go to the Project Overview page:

  1. Click on your iOS app and then the Settings gear icon.
  2. On the Settings page, click the Cloud Messaging tab.
  3. Under the iOS app configuration header, upload your authentication key or certificate(s) using the Upload button.
Sending a test notification

We can test the push notification configuration from the Firebase console. Go to the Cloud Messaging section under the Grow header in the project panel.
Next, click New Notification.
When creating the notification, you only need to specify the following information:

  • The text of the notification.
  • The title (Android only, optional for iOS).
  • The target (either a user segment or topic; I recommend just targeting the iOS or Android app itself: see the following).

Select Publish to send the notification.
Now, you can see the push notification in the notification panel.

Creating a Web API for push notifications

In this section, we will discuss how to create a Web API in ASP.NET MVC for push notifications.
The purpose of this project is to send push notifications through Web API using a registration token stored in the database using the Push Notification plugin.
The Web API is a framework that makes it easy to build HTTP services for browsers and mobile devices. Using ASP.NET, Web API can communicate among different devices from the same database.

Create ASP.NET web application

Follow these steps to create an ASP.NET web app:

  1. First, open Visual Studio and select File >> New >> Project.
  2. Thne, select C# as the language and Windows as the platform.
  3. Select Web as the project type.
  4. Now, select ASP.NET Web Application (.NET Framework) and click Next.
  5. Provide the project name and location. I name my project PushNotificationWebApp. Then, click Create.
  6. Finally, select the Web API template and click Create.

Install necessary NuGet packages

  1. In Visual Studio, choose Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution or right-click your web app and click on Manage NuGet Packages.
  2. Browse for the following NuGet packages and install them:
    • EntityFramework
    • System.Data.SqlClient
    • PushSharp
    • Portable.BouncyCastle

Entity Framework

System.Data.SqlClient

PushSharp

Portable.BouncyCastle


After installing Entity Framework and SQL Server in our app, it will auto-generate the following section of providers inside the entityFramework section in the web.config file.

<entityFramework>
   <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
   </providers>
</entityFramework>

Create a model class

Create a model class with the details of push notifications in the app. Refer to the following code example.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Web;

namespace PushNotificationWebAPIApplication.Models
{
    public class PushNotification
    {
        [Key]
        public int Id { get; set; }
        public string DeviceId { get; set; }
        public string DeviceType { get; set; }
        public DateTime CreatedDate { get; set; }
        public bool IsDeleted { get; set; }
    }

    public class PushNotificationAddResponse
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
    }

    public class ResponseData
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public HttpStatusCode StatusCode { get; set; }
    }

    public class PushNotificationData
    {
        public int Id { get; set; }
        public string DeviceId { get; set; }
        public string DeviceType { get; set; }
    }

    public class PushNotificationResponseList : ResponseData
    {
        public List<PushNotificationData> PushNotification { get; set; }
    }

    public class Notification
    {
        public string NotificationTitle { get; set; }
        public string NotificationBody { get; set; }
    }
}

Creation of DBContext

Create a DBContext file in the app with the following code. This code will point to the connection from the web.config file.

using PushNotificationWebAPIApplication.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace PushNotificationWebAPIApplication.Context
{
    public class PushNotificationDbContext : DbContext
    {
        public PushNotificationDbContext() : base("name=PushNotificationContext")
        {

        }
        public DbSet<PushNotification> PushNotifications { get; set; }
    }
}

Then, create a DatabaseInitializer file with the following code.

using System.Data.Entity;

namespace PushNotificationWebAPIApplication.Context
{
    public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<PushNotificationDbContext>
    {
        protected override void Seed(PushNotificationDbContext context)
        {
            base.Seed(context);
        }
    }
}

Add connection string

Add the following connection string detail to the web.config file. This contains the server details, user ID, password, and database name.

Make sure to use the same connection name added in the DBContext.

<connectionStrings>
    <add connectionString="data source=SYNCLAPN18812;initial catalog=PushNotificationDB;persist security info=True;user id=sa;password=sa@1234" name="PushNotificationDBConnection" providerName="System.Data.SqlClient" />
</connectionStrings>

Add Controller

Now, go to Controller and expand it. By default, there will be some controllers in the Controller directory. To create a new controller:

  1. Select and right-click Controllers >> Add >> Controller.
  2. Then, select Web API, choose Web API 2 Controller – Empty, and click Add.
  3. Then, name the controller PushNotificationController and click Add.

Replace the code inside the controller with the following code.

using System;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using PushNotificationWebAPIApplication.Context;
using PushNotificationWebAPIApplication.Models;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using PushSharp.Core;
using PushSharp.Google;
using System.Configuration;

namespace PushNotificationWebAPIApplication.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/pushNotification")]
    public class PushNotificationController : ApiController
    {
        public string TokenRegisterMessage = "Token was registered successfully";
        public string SendPushNotificationMessage = "Push notification was send successfully";

        private string serverKeyForApp = ConfigurationManager.AppSettings["FCMServerKeyForApp"];
        private string senderIdForApp = ConfigurationManager.AppSettings["FCMSenderIdForApp"];
        private string fcmNotificationSendURL = ConfigurationManager.AppSettings["FCMNotificationSendURL"];

        private string appleKeyId = ConfigurationManager.AppSettings["AppleKeyId"];
        private string appleTeamId = ConfigurationManager.AppSettings["AppleTeamId"];
        private string appId = ConfigurationManager.AppSettings["AppId"];
        private string appleAuthKeyFile = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["AppleAuthKeyFile"]);

        private PushNotificationDbContext dbContext = new PushNotificationDbContext();
        public PushNotificationController() 
        {
        }

        /// <summary>
        /// Add Push Notification
        /// </summary>
        /// <param name="pushNotification"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("addPushNotification")]
        public HttpResponseMessage AddPushNotification(PushNotification pushNotification)
        {
            var pushNotificationAddResponse = new PushNotificationAddResponse();
            try
            {
                var PushNotification = new PushNotification
                {
                    DeviceId = pushNotification.DeviceId,
                    DeviceType = pushNotification.DeviceType,
                    CreatedDate = DateTime.Now,
                    IsDeleted = false
                };

                var pushnotification = (from push in dbContext.PushNotifications
                                                    where push.DeviceId == pushNotification.DeviceId &&
                                                    push.IsDeleted == false
                                            select push).FirstOrDefault();

                if (pushnotification != null)
                {
                    pushnotification.IsDeleted = true;
                    dbContext.SaveChanges();
                }

                dbContext.PushNotifications.Add(PushNotification);
                dbContext.SaveChanges();

                pushNotificationAddResponse.IsSuccess = true;
                pushNotificationAddResponse.Message = TokenRegisterMessage;

                return Request.CreateResponse(HttpStatusCode.OK, pushNotificationAddResponse);
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.Unauthorized, ex.Message);
            }
        }

        /// <summary>
        /// Send Push Notification
        /// </summary>
        /// <param name="pushMessage"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("sendPushNotification")]
        public HttpResponseMessage SendPushNotification(Notification notificationList)
        {
            var pushNotificationAddResponse = new PushNotificationAddResponse();
            try
            {
                var responseStatus = new ResponseData();

                var androidDeviceIdList = new List<string>();
                var iosDeviceIdList = new List<string>();

                var pushNotificationList = GetPushNotification();

                if (pushNotificationList.Count > 0)
                {
                    foreach (var notification in pushNotificationList)
                    {
                        if (notification.DeviceType == "1")
                        {
                            androidDeviceIdList.Add(notification.DeviceId);
                        }
                        else if (notification.DeviceType == "2")
                        {
                            iosDeviceIdList.Add(notification.DeviceId);
                        }
                    }

                    if (androidDeviceIdList.Count > 0)
                    {
                        SendToAndroid(androidDeviceIdList, notificationList.NotificationBody, notificationList.NotificationTitle, serverKeyForApp, senderIdForApp);
                    }

                    if (iosDeviceIdList.Count > 0)
                    {
                        var apns = new IOSPushNotificationHandler(appleKeyId, appleTeamId, appId, appleAuthKeyFile, true);

                        foreach (var token in iosDeviceIdList)
                        {
                            apns.JwtAPNsPush(token, notificationList.NotificationTitle, notificationList.NotificationBody);
                        }
                    }
                }

                pushNotificationAddResponse.IsSuccess = true;
                pushNotificationAddResponse.Message = SendPushNotificationMessage;

                return Request.CreateResponse(HttpStatusCode.OK, pushNotificationAddResponse);
            }
            catch (Exception ex)
            {
                return Request.CreateResponse(HttpStatusCode.Unauthorized, ex.Message);
            }
        }

        private List<PushNotificationData> GetPushNotification()
        {
            var responseStatus = new PushNotificationResponseList();
            var pushNotificationList = new List<PushNotificationData>();
            var pushnotification = (from push in dbContext.PushNotifications
                                    where push.IsDeleted ==false
                                    select push).ToList();

            foreach (var notify in pushnotification)
            {
                var pushNotificationData = new PushNotificationData();
                pushNotificationData.Id = notify.Id;
                pushNotificationData.DeviceId = notify.DeviceId;
                pushNotificationData.DeviceType = notify.DeviceType;

                pushNotificationList.Add(pushNotificationData);
            }
            return pushNotificationList;
        }

        public void SendToAndroid(List<string> deviceIdList, string NotificationBody, string NotificationTitle, string ServerKeyForApp, string SenderIdForApp)
        {
            var config = new GcmConfiguration(SenderIdForApp, ServerKeyForApp, null);
            config.GcmUrl = fcmNotificationSendURL;
            var broker = new GcmServiceBroker(config);
            broker.OnNotificationFailed += (notification, aggregateEx) => GcmNotificationFailed(aggregateEx);
            broker.OnNotificationSucceeded += (notification) => GcmNotificationSucceeded();
            broker.Start();

            broker.QueueNotification(new GcmNotification
            {
                RegistrationIds = deviceIdList,
                Notification = JObject.FromObject(new
                {
                    body = NotificationBody,
                    title = NotificationTitle,
                    badge = 1
                }),
                Priority = GcmNotificationPriority.High,
                ContentAvailable = true
            });

            broker.Stop();
        }

        private void GcmNotificationSucceeded()
        {
            GetDevicePushStatus("Notification has been sent");
        }

        private void GcmNotificationFailed(AggregateException aggregateEx)
        {
            string errorMessage = "";
            aggregateEx.Handle(ex =>
            {
                if (ex is GcmNotificationException)
                {
                    var notificationException = (GcmNotificationException)ex;
                    var gcmNotification = notificationException.Notification;
                    var description = notificationException.Description;
                    if (description != null)
                        errorMessage = "Notification Failed: Desc={" + description + "}";
                    else
                        errorMessage = "Notification Failed: Desc={" + notificationException.Message + "}";
                }
                else if (ex is GcmMulticastResultException)
                {
                    var multicastException = (GcmMulticastResultException)ex;

                    foreach (var succeededNotification in multicastException.Succeeded)
                    {
                    }

                    foreach (var failedKvp in multicastException.Failed)
                    {
                        var n = failedKvp.Key;
                        var e = failedKvp.Value;
                        errorMessage += "Notification Failed: Desc={" + e.Message + "}";
                    }

                }
                else if (ex is DeviceSubscriptionExpiredException)
                {
                    var expiredException = (DeviceSubscriptionExpiredException)ex;

                    var oldId = expiredException.OldSubscriptionId;
                    var newId = expiredException.NewSubscriptionId;

                    errorMessage = "Device RegistrationId Expired: {" + oldId + "}";

                    if (!string.IsNullOrWhiteSpace(newId))
                    {
                        errorMessage = "Device RegistrationId Changed To: {" + newId + "}";
                    }
                }
                else if (ex is RetryAfterException)
                {
                    var retryException = (RetryAfterException)ex;
                    errorMessage = "FCM Rate Limited, don't send more until after {" + retryException.RetryAfterUtc + "}";
                }
                else if (ex is UnauthorizedAccessException)
                {
                    var unauthorizedexception = (UnauthorizedAccessException)ex;
                    errorMessage = "FCM Authorization Failed.";
                }
                else
                {
                    errorMessage = "Notification Failed for some unknown reason";
                }

                return true;
            });
            GetDevicePushStatus(errorMessage);
        }

        private string GetDevicePushStatus(string statusText)
        {
            return statusText;
        }   
    }
}

Create IOSPushNotificationHandler and Http2CustomHandler class files. Refer to the following code examples.

IOSPushNotificationHandler.cs

using Jose;
using Jose.keys;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace PushNotificationWebAPIApplication.Models
{
    public class IOSPushNotificationHandler
    {
        public string Algorithm { get; set; }
        public string HostServerUrl { get; set; }
        public int HostPort { get; set; }
        public string APNsKeyId { get; set; }
        public string TeamId { get; set; }
        public string BundleAppId { get; set; }
        public string AppleAuthKeyFile { get; set; }
        public CngKey PrivateKey { get; set; }
        public string AccessToken { get; set; }

        public IOSPushNotificationHandler(string appleKeyId, string appleTeamId, string appId, string appleAuthKeyFile, bool production)
        {
            Algorithm = "ES256";

            if (production == false)
                HostServerUrl = "api.development.push.apple.com";
            else
                HostServerUrl = "api.push.apple.com";

            HostPort = 443;

            APNsKeyId = appleKeyId;
            TeamId = appleTeamId;
            BundleAppId = appId;
            AppleAuthKeyFile = appleAuthKeyFile;
        }

        private long ToUnixEpochDate(DateTime date)
        {
            return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
        }

        private async Task JwtAPNsPush(Uri host_uri, string access_token, byte[] payload_bytes)
        {
            var result = new ResponseData();

            try
            {
                using (var handler = new Http2CustomHandler())
                {
                    using (var http_client = new HttpClient(handler))
                    {
                        var request_message = new HttpRequestMessage();
                        {
                            request_message.RequestUri = host_uri;
                            request_message.Headers.Add("authorization", String.Format("bearer {0}", access_token));
                            request_message.Headers.Add("apns-id", Guid.NewGuid().ToString());
                            request_message.Headers.Add("apns-expiration", "0");
                            request_message.Headers.Add("apns-priority", "10");
                            request_message.Headers.Add("apns-topic", BundleAppId);
                            request_message.Method = HttpMethod.Post;
                            request_message.Content = new ByteArrayContent(payload_bytes);
                        }

                        var response_message = await http_client.SendAsync(request_message);
                        if (response_message.StatusCode == System.Net.HttpStatusCode.OK)
                        {
                            var response_uuid = "";

                            IEnumerable<string> values;
                            if (response_message.Headers.TryGetValues("apns-id", out values))
                            {
                                response_uuid = values.First();

                                result.Message = $"success: '{response_uuid}'";
                                result.IsSuccess = true;
                            }
                            else
                            {
                                result.Message = "failure";
                            }
                        }
                        else
                        {
                            var response_body = await response_message.Content.ReadAsStringAsync();
                            var response_json = JObject.Parse(response_body);

                            var reason_str = response_json.Value<string>("reason");
                            result.Message = $"failure: '{reason_str}'";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.Message = $"exception: '{ex.Message}'";
            }
        }

        private CngKey GetPrivateKey()
        {
            using (var reader = File.OpenText(AppleAuthKeyFile))
            {
                var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
                var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();
                var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();
                var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
                return EccKey.New(x, y, d);
            }
        }

        private void JwtAPNsPushExtend(string device_token, string title, string body)
        {
            var host_uri = new Uri($"https://{HostServerUrl}:{HostPort}/3/device/{device_token}");

            var _payload = new Dictionary<string, object>()
                    {
                        { "iss", TeamId },
                        { "iat", ToUnixEpochDate(DateTime.Now) }
                    };

            var header = new Dictionary<string, object>()
                    {
                        { "alg", Algorithm },
                        { "kid", APNsKeyId }
                    };

            var privateKey = GetPrivateKey();

            AccessToken = JWT.Encode(_payload, privateKey, JwsAlgorithm.ES256, header);

            var payload = new byte[0];
            {
                var data = JObject.FromObject(new
                {
                    aps = new
                    {
                        alert = new
                        {
                            title = title,
                            body = body
                        },
                        badge = 1
                    }
                });

                payload = System.Text.Encoding.UTF8.GetBytes(data.ToString());
            }

            Task.Run(async () => await JwtAPNsPush(host_uri, AccessToken, payload));
        }

        public void JwtAPNsPush(string device_token, string title, string body)
        {
            JwtAPNsPushExtend(device_token, title, body);
        }
    }

}

Http2CustomHandler.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace PushNotificationWebAPIApplication.Models
{
    public class Http2CustomHandler : WinHttpHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            request.Version = new Version("2.0");
            return base.SendAsync(request, cancellationToken);
        }
    }
}

Web config

Maintain the following app ID, FCM server key and sender ID, Apple key ID, team ID, and auth key file details in the web.config file.

<add key="AppId" value="com.pushnotification.app" />

<!-- Android Config Push Notification - FCM -->
<add key="FCMServerKeyForApp" value="*************************" />
<add key="FCMSenderIdForApp" value="***************" />
<add key="FCMNotificationSendURL" value="https://fcm.googleapis.com/fcm/send" />

<!-- IOS Config Push Notification -->
<add key="AppleKeyId" value="ABC1DEF11G" />
<add key="AppleTeamId" value="ER342D23FD9" />
<add key="AppleAuthKeyFile" value="AuthKey_ABC1DEF11G.p8" />

Android config

Click the gear icon at the top left and select the Project settings of the Firebase Console. Select the Cloud Messaging tab.

Under Cloud Messaging API (Legacy), click the options (three dots on the right) and then click Manage API in Google Cloud Console. This will open a new page. Under Cloud Messaging, press Enable.

Refresh the page on your Firebase Console and copy your Server key and Sender ID.

IOS config

Include the AuthKey_ ABC1DEF11G.p8 file in the app we created in the previous section.

The Apple key ID can also be derived from the name of the file for the key. Your file with the key will probably be named something like AuthKey_ABC1DEF11G.p8, so ABC1DEF11G is the key ID.

The team ID can be found in the upper-right corner of the Apple account webpage.

Resources

For more details, refer to the complete project in the Firebase Push Notification for Android and IOS repository.

Conclusion

I hope this blog provided a clear idea about how to build Ionic with Angular apps for implementing Firebase push notifications for both Android and iOS and creating ASP.NET projects with SQL to send Firebase push notifications.

Related blogs

Meet the Author

Sangeetha Periyaiah

Sangeetha Periyaiah is a Software Engineer at Syncfusion for Consulting Projects and has been active in development since 2017. She is passionate about exploring new technologies. She is currently working as a full-stack developer in ASP.NET MVC, which uses SQL as the backend.