How can I implement push notification in Xamarin forms

SreeeeSreeee INMember ✭✭✭✭✭
edited February 26 in Xamarin.Forms

I am going to implement push notification for my Xamarin Forms project targeting IOS, Android and UWP.

Is there any Nuget Package available for this feature? Or this feature requires platform-specific implementation? And is push notification works on UWP?

What is Azure Notification Hubs? Is that necessary to implement push notification in xamarin forums?

I found a blog about this feature here, but in that the notifications are sending from Azure portal. My project was a chat application and the push notification should come when a new message sends.

Any help would be greatly appreciated. Sample codes or tutorial references are welcome.

Best Answers

Answers

  • SreeeeSreeee INMember ✭✭✭✭✭

    Hi @LandLu Thanks for the documentation.

    I have some doubts, Is Azure Notification Hubs are free? In this documentation, the notifications are sending from the Azure portal. My project was a chat application and the push notification should come when a new message sends. How can I handle that from back-end? Our back end is Java.

  • LandLuLandLu Member, Xamarin Team Xamurai

    I have some doubts, Is Azure Notification Hubs are free?

    Yes, it's free. It based on the APNS for iOS and FCM for Android. And they are the Apple's and Google's services.
    I just post the sample for the client side. You should connect your own server to Azure's service. You could use asp.net or jave to build your server, it's up to you.
    When a message comes to the client side, it must be sent from your back-end. You should notify the Azure's service to send a notification too at the same time.

  • SreeeeSreeee INMember ✭✭✭✭✭

    ok @LandLu

    My friend suggested me to do this feature using AWS. Is this possible?

    Which method is the simplest? AWS or Azure?

    Totally confused after reading the documents and didn't started the implementation after 2 days. o:) o:) o:) Now I don't know from where to start this feature.

  • LandLuLandLu Member, Xamarin Team Xamurai

    I haven't used ASW before. I think it's not difficult to implement Azure for notifications.
    And this is a forum for Microsft Xamarin. If you faced issues when you are implementing Azure. You could ask it both here and on Azure's forum.
    But for AWS. I've no idea about that.

  • SreeeeSreeee INMember ✭✭✭✭✭

    Hi @LandLu I have started implementing push notification.

    But the device token is not getting generating. I have added following code in MainActivity:

    [Service]
        [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
        public class FirebaseRegistrationService : FirebaseInstanceIdService
        {
            const string TAG = "FirebaseRegistrationService";
    
            public override void OnTokenRefresh()
            {
                var refreshedToken = FirebaseInstanceId.Instance.Token;
                Console.WriteLine($"Token received: {refreshedToken}");
                SendRegistrationToken(refreshedToken);
            }
    
            void SendRegistrationToken(string token)
            {
                //Sending token to database
            }
        } 
    

    But getting following message on the output box:

    FirebaseInstanceId(26302): background sync failed: SERVICE_NOT_AVAILABLE, retry in 30s

  • SreeeeSreeee INMember ✭✭✭✭✭

    @Sreeee said:
    Hi @LandLu I have started implementing push notification.

    But the device token is not getting generating. I have added following code in MainActivity:

    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class FirebaseRegistrationService : FirebaseInstanceIdService
    {
    const string TAG = "FirebaseRegistrationService";

    public override void OnTokenRefresh()
    {
    var refreshedToken = FirebaseInstanceId.Instance.Token;
    Console.WriteLine($"Token received: {refreshedToken}");
    SendRegistrationToken(refreshedToken);
    }

    void SendRegistrationToken(string token)
    {
    //Sending token to database
    }
    }

    But getting following message on the output box:

    FirebaseInstanceId(26302): background sync failed: SERVICE_NOT_AVAILABLE, retry in 30s

    Update: This was the issue with the device. When I change the device the tokens are generating. Now I am trying to send a sample notification from the FCM console.

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee Glad you solved it. If you have other issues please feel free to open a new thread here.

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu said:
    @Sreeee Glad you solved it. If you have other issues please feel free to open a new thread here.

    I received the token and now I am trying to send a test notification to my device. Is that done from the FCM or Amazon SNS? From where I need to send the push notification?

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee It depends on what libraries are you using, I recommend FCM.

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu I tried sending from FCM, but not delivering. :)

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited March 6

    @LandLu Received a test notification to my device from FCM.

    The problem was with the device token, every time different tokens are generating for the same device. I keep tried with the initial token.

    I think for a device the tokens are always constant. Why different tokens are generating for the same device?

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited March 6

    @LandLu I have another doubt, can you please clear it?

    I am sending the delivered message to the main project from android project via MessagingCenter like below:

    MessagingCenter.Send<object, string>(this, SamplePushNotification.App.NotificationReceivedKey, msg);

    How can I subscribe to this in MainPage.xaml.cs? I tried like below, don't know the exact syntax: How can I add OnMessageReceived as the last argument?

    protected override void OnAppearing()
            {
                base.OnAppearing();
    
                //MessagingCenter.Subscribe<object, string>(this, App.NotificationReceivedKey, OnMessageReceived(this,msg));
            }
    
            void OnMessageReceived(object sender, string msg)
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    lblmsg.Text = msg;
                });
            }
    

    I saw this method in this tutorial video.

  • LandLuLandLu Member, Xamarin Team Xamurai
    Accepted Answer

    @Sreeee Try this:

    MessagingCenter.Subscribe<object, string>(this, App.NotificationReceivedKey, OnMessageReceived);
    
    void OnMessageReceived(object sender, string msg)
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            lblmsg.Text = msg;
        });
    }
    
  • SreeeeSreeee INMember ✭✭✭✭✭

    @Sreeee said:
    @LandLu Received a test notification to my device from FCM.

    The problem was with the device token, every time different tokens are generating for the same device. I keep tried with the initial token.

    I think for a device the tokens are always constant. Why different tokens are generating for the same device?

    Can you please clear this doubt? Why different tokens are generating for the same device?

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu I added codes to my ios project for handling the notification. But stuck on an issue on RequestPushPermissionsAsync().

    I got this codes from this tutorial.

  • LandLuLandLu Member, Xamarin Team Xamurai
    Accepted Answer

    @Sreeee said:

    @Sreeee said:
    @LandLu Received a test notification to my device from FCM.

    The problem was with the device token, every time different tokens are generating for the same device. I keep tried with the initial token.

    I think for a device the tokens are always constant. Why different tokens are generating for the same device?

    Can you please clear this doubt? Why different tokens are generating for the same device?

    If the app hasn't been uninstalled, yes the token won't be changed. You could get that token by FirebaseInstanceId.Instance.Token;.
    But if you reinstalled your app every time you debugged the project, the OnTokenRefresh would be fired and the token changed. This can't be used as the device's unique identifier.
    You can refer to this documentation to understand when will the OnTokenRefresh be called:
    https://docs.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=windows#implement-the-firebase-instance-id-service

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee said:
    @LandLu I added codes to my ios project for handling the notification. But stuck on an issue on RequestPushPermissionsAsync().

    I got this codes from this tutorial.

    This is far away from this case's topic we talk about at early time. I think you should open a new thread, we could discuss this issue there. And this is better for other community members to participate in it or understand the same issue.

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu said:

    @Sreeee said:
    @LandLu I added codes to my ios project for handling the notification. But stuck on an issue on RequestPushPermissionsAsync().

    I got this codes from this tutorial.

    This is far away from this case's topic we talk about at early time. I think you should open a new thread, we could discuss this issue there. And this is better for other community members to participate in it or understand the same issue.

    I will start a new thread for this. :)

  • SreeeeSreeee INMember ✭✭✭✭✭
    edited March 11 Accepted Answer

    @LandLu The last issue gets resolved by adding an await like below:

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu I am getting NullReferenceException when doing push notification in UWP, I have started a new thread here. Can you please help?

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu Is it possible to call Azure Notification Hub vai Postman?

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee If you want to use Azure to test notification, you could use its Test Send:

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu How can I open a specific content page when clicking a notification?

  • LandLuLandLu Member, Xamarin Team Xamurai

    @Sreeee What libraries are you using now to implement the notifications?

  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu said:
    @Sreeee What libraries are you using now to implement the notifications?

    I am using FCM for android and IOS. Not implementing UWP now. I am not using Azure or AWS now.

  • LandLuLandLu Member, Xamarin Team Xamurai
    @Sreeee I have something emergency to do today. Will update you later.
  • SreeeeSreeee INMember ✭✭✭✭✭

    @LandLu said:
    @Sreeee I have something emergency to do today. Will update you later.

    Ok thanks :)

  • SreeeeSreeee INMember ✭✭✭✭✭

    Hi, @LandLu Are you free today? Can you help me to open a content page when tapping the notification?

    I have added only 'OnMessageReceived' function, but when tapping notification code execution not entering that part. Didn't implement any notification handler, how can I do that?

    I have started a new thread for this.

  • ArifShaikArifShaik Member

    Hi Sreeee, Can u send me the reference code for push notification using FCM ??plzz

  • SreeeeSreeee INMember ✭✭✭✭✭

    @ArifShaik
    AppDelegate.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Firebase.CloudMessaging;
    using Foundation;
    using ImageCircle.Forms.Plugin.iOS;
    using MessageBar;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using UIKit;
    using UserNotifications;
    using Xamarin.Forms;
    
    namespace SmartWCM.iOS
    {
        // The UIApplicationDelegate for the application. This class is responsible for launching the 
        // User Interface of the application, as well as listening (and optionally responding) to 
        // application events from iOS.
        [Register("AppDelegate")]
        public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
        {
            //
            // This method is invoked when the application has loaded and is ready to run. In this 
            // method you should instantiate the window, load the UI into it and then make the window
            // visible.
            //
            // You have 17 seconds to return from this method, or iOS will terminate your application.
            //
            public override bool FinishedLaunching(UIApplication app, NSDictionary options)
            {
                global::Xamarin.Forms.Forms.Init();
                LoadApplication(new App());
    
                #region Push Notification            
                Firebase.Core.App.Configure();
    
                // Register your app for remote notifications.
                if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
                {
                    // iOS 10 or later
                    var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
                    UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) => {
                        Console.WriteLine(granted);
                    });
    
                    // For iOS 10 display notification (sent via APNS)
                    UNUserNotificationCenter.Current.Delegate = this;
    
                    // For iOS 10 data message (sent via FCM)
                    //Messaging.SharedInstance.RemoteMessageDelegate = this;
                }
                else
                {
                    // iOS 9 or before
                    var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
                    var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
                    UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
                }
    
                UIApplication.SharedApplication.RegisterForRemoteNotifications();
    
                Messaging.SharedInstance.Delegate = this;
                Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
                #endregion
    
                return base.FinishedLaunching(app, options);
            }
    
            [Export("messaging:didReceiveRegistrationToken:")]
            public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
            {
                // Monitor token generation: To be notified whenever the token is updated.
                LogInformation(nameof(DidReceiveRegistrationToken), $"Firebase registration token: {fcmToken}");
    
                // TODO: If necessary send token to the application server.
                    //I am passing fcmtoken through MessagingCenter and sending the token to the server
                // Note: This callback is fired at each app startup and whenever a new token is generated.
                MessagingCenter.Send<object, string>(this, "fcmtoken", fcmToken.ToString());
                Console.WriteLine($"fcmtoken received: {fcmToken}");
            }
    
            public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
            {
                // Handle Notification messages in the background and foreground.
                // Handle Data messages for iOS 9 and below.
                // If you are receiving a notification message while your app is in the background,
                // this callback will not be fired till the user taps on the notification launching the application.
                // TODO: Handle data of notification
                // With swizzling disabled you must let Messaging know about the message, for Analytics
                //Messaging.SharedInstance.AppDidReceiveMessage (userInfo);
                HandleMessage(userInfo);
    
                // Print full message.
                LogInformation(nameof(DidReceiveRemoteNotification), userInfo);
    
                completionHandler(UIBackgroundFetchResult.NewData);
    
                var myData = JsonConvert.DeserializeObject<List<keyword>>(userInfo[new NSString("keyword")] as NSString);
                Console.WriteLine($"myData received: {myData}");
                if (UIApplication.SharedApplication.ApplicationState.Equals(UIApplicationState.Active))
                {
                    //App is in foreground. Act on it.
                }
                else
                {
                    MessagingCenter.Send<object, List<key>>(this, "messagedata", myData);
                }
            }
    
            //User for showing the notification in foreground mode
            [Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
            public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification,
    Action<UNNotificationPresentationOptions> completionHandler)
            {
                completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
            }
    
                 //Foreground mode notification tapping handling here
            [Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
            public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action
            completionHandler)
            {
                completionHandler();
                NSDictionary userInfo = response.Notification.Request.Content.UserInfo;
                var myData = JsonConvert.DeserializeObject<List<keyword>>(userInfo[new NSString("keyword")] as NSString);
                Console.WriteLine($"myData received: {myData}");
                MessagingCenter.Send<object, List<key>>(this, "messagedata", myData);
            }
    
            [Export("messaging:didReceiveMessage:")]
            public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
            {
                // Handle Data messages for iOS 10 and above.
                HandleMessage(remoteMessage.AppData);
    
                LogInformation(nameof(DidReceiveMessage), remoteMessage.AppData);
            }
    
            void HandleMessage(NSDictionary message)
            {
                //if (MessageReceived == null)
                //    return;
    
                //MessageType messageType;
                //if (message.ContainsKey(new NSString("aps")))
                //    messageType = MessageType.Notification;
                //else
                //    messageType = MessageType.Data;
    
                //var e = new UserInfoEventArgs(message, messageType);
                //MessageReceived(this, e);
            }
    
            public static void ShowMessage(string title, string message, UIViewController fromViewController, Action actionForOk = null)
            {
                var alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
                alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, (obj) => actionForOk?.Invoke()));
                fromViewController.PresentViewController(alert, true, null);
            }
    
            void LogInformation(string methodName, object information) => Console.WriteLine($"\nMethod name: {methodName}\nInformation: {information}");
    
            async Task RequestPushPermissionAsync()
            {
                // iOS10 and later (https://developer.xamarin.com/guides/ios/platform_features/user-notifications/enhanced-user-notifications/#Preparing_for_Notification_Delivery)
                // Register for ANY type of notification (local or remote):
                var requestResult = await UNUserNotificationCenter.Current.RequestAuthorizationAsync(
                    UNAuthorizationOptions.Alert
                    | UNAuthorizationOptions.Badge
                    | UNAuthorizationOptions.Sound);
    
    
                // Item1 = approved boolean
                bool approved = requestResult.Item1;
                NSError error = requestResult.Item2;
                if (error == null)
                {
                    // Handle approval
                    if (!approved)
                    {
                        Console.Write("Permission to receive notifications was not granted.");
                        return;
                    }
    
                    var currentSettings = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
                    if (currentSettings.AuthorizationStatus != UNAuthorizationStatus.Authorized)
                    {
                        Console.WriteLine("Permissions were requested in the past but have been revoked (-> Settings app).");
                        return;
                    }
    
                    UIApplication.SharedApplication.RegisterForRemoteNotifications();
                }
                else
                {
                    Console.Write($"Error requesting permissions: {error}.");
                }
            }
        }
    }
    
Sign In or Register to comment.