You should queue a separate notification for each item to process.
It is not possible set multiple device tokens on a single notification. The OperationCanceledException will occur, because you do.
How to send a push notification to more than one device (iOS)?
-
05-12-2021 - |
Question
I'm trying to optimize the push notifications on my server. For now I send those one by one (with an old library) and it takes a while (4 hours).
I refactored my service to send a notification with a lot of device tokens (For now I tried with batches of 500 tokens). For that I'm using the Redth/PushSharp library. I followed the sample code then I adapted it to send the notifications to severals device tokens.
PushService service = new PushService();
//Wire up the events
service.Events.OnDeviceSubscriptionExpired += new PushSharp.Common.ChannelEvents.DeviceSubscriptionExpired(Events_OnDeviceSubscriptionExpired);
service.Events.OnDeviceSubscriptionIdChanged += new PushSharp.Common.ChannelEvents.DeviceSubscriptionIdChanged(Events_OnDeviceSubscriptionIdChanged);
service.Events.OnChannelException += new PushSharp.Common.ChannelEvents.ChannelExceptionDelegate(Events_OnChannelException);
service.Events.OnNotificationSendFailure += new PushSharp.Common.ChannelEvents.NotificationSendFailureDelegate(Events_OnNotificationSendFailure);
service.Events.OnNotificationSent += new PushSharp.Common.ChannelEvents.NotificationSentDelegate(Events_OnNotificationSent);
service.Events.OnChannelCreated += new PushSharp.Common.ChannelEvents.ChannelCreatedDelegate(Events_OnChannelCreated);
service.Events.OnChannelDestroyed += new PushSharp.Common.ChannelEvents.ChannelDestroyedDelegate(Events_OnChannelDestroyed);
//Configure and start ApplePushNotificationService
string p12Filename = ...
string p12FilePassword = ...
var appleCert = File.ReadAllBytes(p12Filename);
service.StartApplePushService(new ApplePushChannelSettings(true, appleCert, p12FilePassword));
var appleNotification = NotificationFactory.Apple();
foreach (var itemToProcess in itemsToProcess)
{
itemToProcess.NotificationDateTime = DateTime.Now;
mobile.SubmitChanges();
string deviceToken = GetCleanDeviceToken(itemToProcess.MobileDevice.PushNotificationIdentifier);
appleNotification.ForDeviceToken(deviceToken);
}
service.QueueNotification(appleNotification
.WithAlert(itemsToProcess[0].MobileDeviceNotificationText.Text)
.WithSound("default")
.WithBadge(0)
.WithCustomItem("View", itemsToProcess[0].Value.ToString()));
//Stop and wait for the queues to drains
service.StopAllServices(true);
Then I tried to send 3 notifications to 2 devices. Only the first device got them (and the problem is not device-related because I tried with both of them separately). Right after that an OperationCanceledException is thrown in the PushChannelBase class. So I don't know what's wrong. Any idea?
Solution
OTHER TIPS
Example: Console C# Application
This assumes
- you have valid production and development certificates
- you have stored multiple device tokens within your database
- you have a notification that comes from your database
You are using PushSharp Library
using System; using System.Collections.Generic; using System.Linq; using System.Text; using PushSharp; using PushSharp.Core; using PushSharp.Apple; using System.IO; namespace MyNotification { class Program { //args may take "true" or "false" to indicate the app is running for //development or production (Default = false which means Development) static void Main(string[] args) { bool isProduction = false; if (args != null && args.Length == 1) { Console.Write(args[0] + Environment.NewLine); bool.TryParse(args[0], out isProduction); } try { //Gets a notification that needs sending from database AppNotification notification = AppNotification.GetNotification(); if (notification != null && notification.ID > 0) { //Gets all devices to send the above notification to List<IosDevice> devices = IosDevice.GetDevices(!isProduction); if (devices != null && devices.Count > 0) { PushBroker push = new PushBroker();//a single instance per app //Wire up the events for all the services that the broker registers push.OnNotificationSent += NotificationSent; push.OnChannelException += ChannelException; push.OnServiceException += ServiceException; push.OnNotificationFailed += NotificationFailed; push.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired; push.OnChannelCreated += ChannelCreated; push.OnChannelDestroyed += ChannelDestroyed; //make sure your certifcates path are all good string apnsCertFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../Certificate/Certificates_Apple_Push_Production.p12"); if (!isProduction) apnsCertFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../Certificate/Certificates_Apple_Push_Development.p12"); var appleCert = File.ReadAllBytes(apnsCertFile); push.RegisterAppleService(new ApplePushChannelSettings(isProduction, appleCert, "135TrID35")); //Extension method foreach (IosDevice device in devices) { //if it is required to send additional information as well as the alert message, uncomment objects[] and WithCustomItem //object[] obj = { "North", "5" }; push.QueueNotification(new AppleNotification() .ForDeviceToken(device.DeviceToken) .WithAlert(DateTime.Now.ToString())//(notification.AlertMessage) //.WithCustomItem("Link", obj) .WithBadge(device.BadgeCount + 1) .WithSound(notification.SoundFile));//sound.caf } push.StopAllServices(waitForQueuesToFinish: true); } } Console.WriteLine("Queue Finished, press return to exit..."); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.ReadLine(); } } static void NotificationSent(object sender, INotification notification) { Console.WriteLine("Sent: " + sender + " -> " + notification); } static void NotificationFailed(object sender, INotification notification, Exception notificationFailureException) { Console.WriteLine("Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification); } static void ChannelException(object sender, IPushChannel channel, Exception exception) { Console.WriteLine("Channel Exception: " + sender + " -> " + exception); } static void ServiceException(object sender, Exception exception) { Console.WriteLine("Service Exception: " + sender + " -> " + exception); } static void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification) { Console.WriteLine("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId); } static void ChannelDestroyed(object sender) { Console.WriteLine("Channel Destroyed for: " + sender); } static void ChannelCreated(object sender, IPushChannel pushChannel) { Console.WriteLine("Channel Created for: " + sender); } } }