How to Use Push Notifications in Xamarin Forms

Send Push Notification In Xamarin Forms Without Firebase

Hopefully this is your request:

We don't have rights to place employee data on cloud servers or cloud
databases.

And not this:

Send Push Notification In Xamarin Forms Without Firebase

As they are entirely different. If you want to send data to Android devices you need to use Firebase for that just like you need to use Apple's servers for iOS devices. However you don't need to have any database or store data on them.

You can use PushSharp nuget if you want to minimize 3rd party server usage or some other similar solution.

Just for the record using Firebase (or other services like Azure) to send push notifications doesn't require you to place employee data on cloud servers either. Firebase and other solutions contain range of services that can be used independently and it seems that you consider them as all or nothing solution.

How to implement Local Push notification in Xamarin Android

What you need is push notifications not local notifications. Local notifications only work if your app is running. Push notifications are handled by OS (Android in this case). Xamarin is recommending using Firebase service to handle it. Procedure is pretty straight forward all what you have to do is follow Tutorial from official Xamarin site.

Remote Notifications with Firebase Cloud Messaging

How to use Push Notifications in Xamarin Forms

I just implemented push notification a few days ago, and I'll share my solution here (based on PushSharp)

Step by step guide:

1) In your shared project, create an Interface called IPushNotificationRegister

public interface IPushNotificationRegister
{
void ExtractTokenAndRegister();
}

This interface is used for fetching the push token and then send it to the server. this Token is unique per device.

2) In Your shared project, you should invoke ExtractTokenAndRegister (using your favorite IOC, I called it right after login).

Android Implementation:

3) Add Receivers for listening to events received by Google GCM service:

a)

[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class GCMBootReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
MyIntentService.RunIntentInService(context, intent);
SetResult(Result.Ok, null, null);
}
}

b)

[assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]

namespace Consumer.Mobile.Droid.PushNotification
{
[BroadcastReceiver(Permission = "com.google.android.c2dm.permission.SEND")]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.REGISTRATION" }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { "com.google.android.gcm.intent.RETRY" }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter (new[]{ Intent.ActionBootCompleted }, Categories = new[]{ Intent.CategoryDefault })]
public class GCMBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
MyIntentService.RunIntentInService(context, intent);
SetResult(Result.Ok, null, null);
}
}
}

c) Add Intent service to process the notification

using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Media;
using Android.OS;
using Android.Support.V4.App;
using Consumer.Mobile.Infra;
using Consumer.Mobile.Services.PushNotification;
using Java.Lang;
using XLabs.Ioc;
using TaskStackBuilder = Android.Support.V4.App.TaskStackBuilder;

namespace Consumer.Mobile.Droid.PushNotification
{
[Service]
public class MyIntentService : IntentService
{
private readonly ILogger _logger;
private readonly IPushNotificationService _notificationService;
private readonly IPushNotificationRegister _pushNotificationRegister;

public MyIntentService()
{
_logger = Resolver.Resolve<ILogger>();
_notificationService = Resolver.Resolve<IPushNotificationService>();
_pushNotificationRegister = Resolver.Resolve<IPushNotificationRegister>();
}

static PowerManager.WakeLock _sWakeLock;
static readonly object Lock = new object();

public static void RunIntentInService(Context context, Intent intent)
{
lock (Lock)
{
if (_sWakeLock == null)
{
// This is called from BroadcastReceiver, there is no init.
var pm = PowerManager.FromContext(context);
_sWakeLock = pm.NewWakeLock(
WakeLockFlags.Partial, "My WakeLock Tag");
}
}

_sWakeLock.Acquire();
intent.SetClass(context, typeof(MyIntentService));
context.StartService(intent);
}

protected override void OnHandleIntent(Intent intent)
{
try
{
Context context = this.ApplicationContext;
string action = intent.Action;

if (action.Equals("com.google.android.c2dm.intent.REGISTRATION"))
{
HandleRegistration(context, intent);
}
else if (action.Equals("com.google.android.c2dm.intent.RECEIVE"))
{
HandleMessage(context, intent);
}
}
finally
{
lock (Lock)
{
//Sanity check for null as this is a public method
if (_sWakeLock != null)
_sWakeLock.Release();
}
}
}

private void HandleMessage(Context context, Intent intent)
{

Intent resultIntent = new Intent(this, typeof(MainActivity));

TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);

var c = Class.FromType(typeof(MainActivity));
stackBuilder.AddParentStack(c);
stackBuilder.AddNextIntent(resultIntent);

string alert = intent.GetStringExtra("Alert");
int number = intent.GetIntExtra("Badge", 0);

var imageUrl = intent.GetStringExtra("ImageUrl");
var title = intent.GetStringExtra("Title");

Bitmap bitmap = GetBitmap(imageUrl);

PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(0, (int)PendingIntentFlags.UpdateCurrent);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.SetAutoCancel(true) // dismiss the notification from the notification area when the user clicks on it
.SetContentIntent(resultPendingIntent) // start up this activity when the user clicks the intent.
.SetContentTitle(title) // Set the title
.SetNumber(number) // Display the count in the Content Info
.SetSmallIcon(Resource.Drawable.Icon) // This is the icon to display
.SetLargeIcon(bitmap)
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
.SetContentText(alert); // the message to display.

// Build the notification:
Notification notification = builder.Build();

// Get the notification manager:
NotificationManager notificationManager =
GetSystemService(Context.NotificationService) as NotificationManager;

// Publish the notification:
const int notificationId = 0;
notificationManager.Notify(notificationId, notification);
}

private void HandleRegistration(Context context, Intent intent)
{
var token = intent.GetStringExtra("registration_id");
_logger.Info(this.Class.SimpleName, "Received Token : " + token);

if (_pushNotificationRegister.ShouldSendToken(token))
{
var uid = Android.Provider.Settings.Secure.GetString(MainActivity.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
_notificationService.AddPushToken(token, DeviceUtils.GetDeviceType(), uid);
}
}

private Bitmap GetBitmap(string url)
{

try
{
System.Net.WebRequest request =
System.Net.WebRequest.Create(url);
System.Net.WebResponse response = request.GetResponse();
System.IO.Stream responseStream =
response.GetResponseStream();
return BitmapFactory.DecodeStream(responseStream);

}
catch (System.Net.WebException)
{
return null;
}

}

}
}

d) Implement the Interface IPushNotificationRegister:

using Android.App;
using Android.Content;
using Consumer.Mobile.Services;
using Consumer.Mobile.Services.PushNotification;
[assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]

// Gives the app permission to register and receive messages.
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]

// Needed to keep the processor from sleeping when a message arrives
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
[assembly: UsesPermission(Name = "android.permission.RECEIVE_BOOT_COMPLETED")]
namespace Consumer.Mobile.Droid.PushNotification
{
public class PushNotificationRegister : IPushNotificationRegister
{
public override void ExtractTokenAndRegister()
{
string senders = AndroidConfig.GCMSenderId;
Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER");
intent.SetPackage("com.google.android.gsf");
intent.PutExtra("app", PendingIntent.GetBroadcast(MainActivity.Context, 0, new Intent(), 0));
intent.PutExtra("sender", senders);
MainActivity.Context.StartService(intent);
}

}
}

iOS implementation:

4) In your AppDelegate, add the following method:

a)

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var deviceTokenString = deviceToken.ToString().Replace("<","").Replace(">", "").Replace(" ", "");
var notificationService = Resolver.Resolve<IPushNotificationService>();
var pushNotificationRegister = Resolver.Resolve<IPushNotificationRegister>();

if (pushNotificationRegister.ShouldSendToken(deviceTokenString))
{
var uid = UIDevice.CurrentDevice.IdentifierForVendor.AsString();
notificationService.AddPushToken(deviceTokenString, DeviceUtils.GetDeviceType(), uid);
}
}

b) Implement IPushNotificationRegister :

using Consumer.Mobile.Services;
using Consumer.Mobile.Services.PushNotification;
using UIKit;

namespace Consumer.Mobile.iOS.PushNotification
{
public class iOSPushNotificationRegister : IPushNotificationRegister
{
public override void ExtractTokenAndRegister()
{
const UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}
}

Regarding WP, I didn't implement it.

If you need the code on the server side using PushSharp, let me know.

You can check the client samples I've based my solution here

PUSH Notifications in Xamarin Forms with Microsoft.AppCenter

You don't have the possibility to get Firebase id from using AppCenter.

You have two ways out of this:

1. Skip using AppCenter for pushes in your app. You'd have to implement interaction with Firebase on each platform you support according to documentation. I can share more tips if you choose this option.

2. Convince backend team to use AppCenter API to send individual push notifications https://openapi.appcenter.ms/#/push/Push_Send

I know each of these solutions contradicts one of your requirements. But you'd have to choose only one push service, either AppCenter or Firebase, for your project and stick with it. Personally I vote for Firebase, learned it the hard way.

UPDATED

Most of the time, Firebase documentation is your best friend when switching to FCM.

On the app side:

Setting up Firebase client for iOS and for Android.

Some tricks from my experience:

Android:

You'll have to listen to Firebase InstanceID changes in a descendant of FirebaseMessagingService and store it for later use. It's not available whenever you need it as it is with AppCenter.

My activity has LaunchMode = LaunchMode.SingleTopand I override two methods to handle push depending on app state when it arrived: void OnNewIntent(Intent intent) and void OnPostCreate(Bundle savedInstanceState). It might not be your case because I handle silent pushes as well. To check if intent contains a push from Firebase run intent?.GetStringExtra("google.message_id") != null.

iOS:

Push notifications won't work on iOS simulators and initialization steps might crash your app when run on Simulator. I have created __IOS_SIMULATOR__ constant next to __IOS__ and DEBUG under Debug|iPhoneSimulator configuration in csproj file. And used it in AppDelegate.cs like this:

#if !__IOS_SIMULATOR__
new FirebaseCloudMessagingInitializer().Init();
#endif

AppDelegate offers two methods to override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) and void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler). I used the first one at the beginning and faced edge cases when it was not called. So I had to switch to overriding the latter one. But don't follow this advice blindly, check what suits best for your needs.

Also, beware of https://github.com/firebase/firebase-ios-sdk/issues/2438, I'm not sure whether it was already fixed since I had to deal with it.
In case it is still there, apply the fix from https://github.com/firebase/firebase-ios-sdk/issues/2438#issuecomment-469472087

On backend side:

https://fcm.googleapis.com/fcm/send is so called legacy protocol, use HTTP v1 instead

POST https://fcm.googleapis.com/v1/projects/project_name/messages:send. Explore documentation.

How to authorize send request

How to compose push content

SQL Server Push Notifications to Xamarin Forms

Firstly, I'd advise very strongly against a Xamarin.Forms app (or any mobile application, for that matter) connecting directly to database. Write an API to take care of that for you.

To answer your question though, you need to register the application with a push notification provider. On Android, the native provider is Firebase. On iOS, the native provider is Apple Push Notification Service - or APNS for short. However, you can also use Firebase for iOS via a Google supplied package - which Microsoft have released Xamarin bindings for.

Here's a pretty good video showing how to do that (https://www.youtube.com/watch?v=GNBuaAB8nR8), it's quite current (uploaded in 2021) and the guy, as of the time of writing, works for Microsoft.

Once you've got that implemented, you're going to need some way to push a message from your database to Firebase. A simple API would be my suggestion. Firebase will then handle pushing that data down to your app, in the form of a push notification. You can have a read about the Firebase notification message schema here: https://firebase.google.com/docs/cloud-messaging/concept-options

Just to re-iterate what I said at the top. I'd strongly suggest that you do not connect directly to a database from a mobile app. Outside of the many security implications of doing that, there's many design challenges that you'll face with that approach if you ever need to scale the app up.

EDITED: Fixed a typo.

Push notification from local API in xamarin forms

It is feasible.

As Sushi said, you can use WebSockets or other Sockets to keep connect between Apps and Server. When client connected to your server, server will record which app is connecting.

By the way, you can custom notification's url between server and app.Use some special characters to distinguish between your various parameters, such as &/[ ]. You are free to set the header and body style of the message. Also, when your message needs to be encrypted, some encryption methods can be used.

Here is a similar discussion about Building an Android notification server can be refer .

xamarin push notification ios Page navigation not working when push notification click

If the app is running, we don't need to reload app with LoadApplication , we can directly send message to set MainPage in App.cs .

iOS

if (UIApplication.SharedApplication.ApplicationState.Equals(UIApplicationState.Active))
{
MessagingCenter.Send<object>(this,"Hi");
}

Forms

public App()
{
InitializeComponent();

MainPage = new MainPage();

MessagingCenter.Subscribe<object>(this, "Hi", (obj) => {
MainPage = new NavigationPage(new Splash(pageName));
});
}


Related Topics



Leave a reply



Submit