How to use Push Notifications in Xamarin Forms
How to use Push Notifications in Xamarin Forms
I'm an app using Xamarin.Forms targeting IOS, Android and WP 8.
I need push notifications feature in my app.
I have seen the pushsharp demos and seems promising. But all the codes I had seen is done separately for each platform.
I would like it to be done in the Xamarin.Forms project, somewhere in the App.cs so that I don't need to repeat the code for registering device, and handle how push notifications should be processed.
Any help would be greatly appreciated. Sample codes or tutorial references are welcome.
Edit : I implemented it based on Idot's answer. Here is the link for my answer.
Its purely based on azure, and I'm looking for pushsharp. Also its not about Xamarin forms but separate implementation for each platform. But thanks for finding me something to start on.
– Rohit Vipin Mathews
Mar 16 '15 at 5:46
check out HOL On Azure Push notification in Xamrin: onedrive.live.com/…
– Bhaumik Shah
Apr 18 '15 at 5:43
6 Answers
6
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
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).
ExtractTokenAndRegister
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
:
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:
AppDelegate
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
:
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
Thank you. I will certainly have a look at it. No I wont need the server side code.
– Rohit Vipin Mathews
Mar 19 '15 at 10:39
Where are you handling the
DidReceiveRemoteNotification
for IOS?– Rohit Vipin Mathews
Mar 19 '15 at 12:09
DidReceiveRemoteNotification
I didn't, if you want to add custom behavior for showing notifications while the app is running, you'll need to handle it in the AppDelegate class: public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) {}
– IdoT
Mar 19 '15 at 12:38
You have broadcast receiver for android that is why i was curious
– Rohit Vipin Mathews
Mar 19 '15 at 13:41
You are right, I kept BootReceiver as I followed the guide, You can remove it if you're not interested in handling notifications while your app is running.
– IdoT
Mar 19 '15 at 14:29
I have been suggested to use the following plugin by xamarin support and forms.
This plugin works well
https://github.com/rdelrosario/xamarin-plugins/tree/master/PushNotification
Will update the answer once I get it to work.
UPDATE :
I got push notifications working for both iOS and Android.
I used Google Cloud Messaging Client, an excellent component for Android, and didn't have to write much of the code as mentioned in this answer.
My iOS implementation was similar to this, not much code required.
And for Pushing the notifications from the Server I used nuget package of PushSharp.
I didn't implement in WP, as that was not required in my project.
This Xamarin Help on Push Notifications is worth reading if you are going to implement Push Notifications.
Update (June 2018) - Use the following plugin for FCM on iOS and Android, ti supports Xamarin.Forms - FirebasePushNotificationPlugin
Is this solution working with Android 5+? For what I can see the GCM component is making them crash, is that correct?
– Ingenator
Oct 31 '15 at 0:52
I didn't find any issues, I will double check and get back soon. If you are having a crash please share the exact error message.
– Rohit Vipin Mathews
Oct 31 '15 at 5:05
i am really struggling with Xamarin docs for same , my listener is not getting called
– saket kumar
Apr 28 '16 at 12:57
Thanks for this, Do this plugin support for GCM's topics so that server need not to fetch the registration_ids and client application subscribed to a topic.
– Shivang Gupta
Aug 12 '16 at 12:15
@SHIVANGSANGHI - you can add sender ids.
– Rohit Vipin Mathews
Aug 12 '16 at 12:19
In Xamarin Forms you could also use a notifications SDK like Donky (which is the European equivalent to the American Urban Airship); you can easily make a scalable notifications project in a single day, I've twice built WhatsApp clone shells in under 35 minutes each time using this SDK. See http://docs.mobiledonky.com
This is not possible to do in pure Xamarin.Forms but is relatively trivial to implement a solution whereby they can be handled in the App.cs (although this will require platform specific implementations).
Take a look in the IXForms implementation within the Xamarin.Forms.Labs project where the notifications are channeled back to Forms project:
https://github.com/XLabs/Xamarin-Forms-Labs
and more specifically:
https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/src/Platform/XLabs.Platform/Mvvm
There is a blog post recently here about implementing Push Notifications on Xamarin Forms (well each individual platform because there is no Forms based implementation), using Azure Mobile Services.
http://www.xamarinhelp.com/push-notifications/
You might look at the Appboy Component which has support for this out of the box. https://components.xamarin.com/view/appboy-sdk-bindings
As others have said, you can't do generically without some platform-specific components.
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
This question has been addressed in the official Xamarin Forum, have a look: forums.xamarin.com/discussion/20845/…
– Wizche
Mar 14 '15 at 14:07