Tuesday, November 18, 2014

Send Push Notification using C#

Sending Apple Push Notifications from a C# Application



This is quite the experience learning Objective-C and all the quirks to get an application running on an iPad.  I'll say that I really like the experience, and thin that Objective-C is a very interesting language, simultaneously being both archaic and powerful in the way it handles objects and messaging.  
But this post is not about Objective-C.  It is about how to write a C# application that can send messages to a iOS device using Apple Push Notifications (APN's).  The need for having this done in C# is a requirement of the system I have been developing, as all the internal systems are written in .NET and therefore the easiest and most maintainable code for sending messages would be in C#.
As the audience of this post is intended to be C# developers trying to integrate with APNS, there are a few concepts that first must be taken into consideration when working with APN's which are different from most .Net development.  Primary amongst these are that to deploy an iOS application you need to go through several steps in the iOS provisioning portal to generate several certificates for both your development account as well as for authenticating with APN servers.  I'll briefly go over how to accomplish these tasks.  Objective-C applications can directly use the .CER file, but that file does not work in C#/SSL or with APNS-Sharp and you need to convert it to a .P12 file.  In a nutshell the steps you will need to go through to successfully talk to the APN's servers follows these steps:
  1. Register with Apple for an iOS developers certificate
  2. Register the device(s) with Apple so that the devices can be authenticated
  3. Create an application ID which is used to uniquely identify your application to Apple's services
  4. Inform APN services that you want to deliver messages to that application
  5. Provision your application to be allowed to run on specific devices
This entire process is quite a bit different from .Net development and deployment.  These steps are required by Apple for various reasons, primarily of which is security and quality of experience.  You need to prove to Apple that you are a registered developer (step 1), let them know all the devices you will deploy your applicaiton to (step 2), let Apple know your application by name and code signature (steps 3 and 5), which devices are allowed to run your application (step 5), and that you want to them to build channels between your application and APN servers for you to deliver message to your users.
It's worth noting briefly that these steps are required for an enterprise deployment of the application.  Device registration and application provisioning is not strictly needed when deploying your application to the App Store.

Create a CSR file:

In the Applications folder on your Mac, open the Utilities folder and launch Keychain Access.
Within the Keychain Access drop down menu, select Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority.
  • In the Certificate Information window, enter the following information:
    • In the User Email Address field, enter your email address.
    • In the Common Name field, create a name for your private key (e.g., John Doe Dev Key).
    • The CA Email Address field should be left empty.
    • In the "Request is" group, select the "Saved to disk" option.
  • Click Continue within Keychain Access to complete the CSR generating process.


Developer Account
Login to Apple Developer account (www.developer.apple.com)


  1. Certificates → Development → Apple Push Notification service SSL (Sandbox)




  1. Select an Apple ID for your Push SSL Certificate (Sandbox)




  1. Click the 'Continue' button and go to next step.






  1. Select CSR file which is create starting point






  1. Click the “Download” and double click to install.




  1. Open the Utilities folder and launch Keychain Access. Select your bundle name appear on screen.


Note that I have expanded the top level item to show the private key that is part of the certificate. You will need that private key to generate a certificate file for the C# application to use.  Right click on the private key and select export and save the file as a .p12 file, and I'm going to use developer_private_key.p12 as its name.  You will be asked to prive a password for the file which will be used to encrypt the data in the file.  Create a password and save it as it will be needed later.




You also will need to authenticate with OSX as having permission to export the key.  Enter your password so that the .p12 file can be created…









Note: Make sure to edit and install your Provisioning Profile or create new one.



Web Side:

Step 1:

Please add the following External references. Then add to the reference from External reference folder.

  1. PushSharp.Android.dll
  2. PushSharp.Apple.dll
  3. PushSharp.Core.dll
  4. PushSharp.Windows.dll
  5. PushSharp.WindowsPhone.dll

Step 2:

Calling procedure. Please verify the below sample code.

if (pushNotificationDevice != null && pushNotificationDevice.DOS == "ANDROID" && !string.IsNullOrEmpty(pushNotificationDevice.GCMUQId)&&pushNotificationDevice.GCMUQId.Trim() != "(null)"&& pushNotificationDevice.GCMUQId.Trim() != "0")
{
string[] _registrationIds = { pushNotificationDevice.GCMUQId };
SendMultipleNotification(_registrationIds, notificationMessages.ToArray(), _thisNotificatoin.id, _tempEventType,!string.IsNullOrEmpty(_thisNotificatoin.start)?Utility.GetDateTime(_thisNotificatoin.start) : _thisNotificatoin.startdate);
_isMessageSend = true;
}
else if (pushNotificationDevice != null && pushNotificationDevice.DOS == "iOS" && !string.IsNullOrEmpty(pushNotificationDevice.GCMUQId) && pushNotificationDevice.GCMUQId.Trim() != "(null)" && pushNotificationDevice.GCMUQId.Trim() != "0")
{
new PushBrokerHandler().SendNotifications(pushNotificationDevice.DOS, pushNotificationDevice.GCMUQId, _notificationMessage, _thisNotificatoin.id, _tempEventType, !string.IsNullOrEmpty(_thisNotificatoin.start) ? Utility.GetDateTime(_thisNotificatoin.start) : _thisNotificatoin.startdate);
                                   _isMessageSend = true;
}

Step 3:

        Add PushBrokerHandler calss to your project.  Refer the below PusBrokerHandler class.


using PushSharp;

using PushSharp.Core;

using PushSharp.Android;

using PushSharp.Apple;

using System;

using System.Collections.Generic;

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Configuration;
using YourProject.TrialRemainderService;

namespace YourProject.RemainderService
{
   public class PushBrokerHandler
   {
       public void SendNotifications(string DeviceType, string deviceId, string message, long eventId, string eventType, DateTime startDate)
       {
           //Create our push services broker
           var push = new PushBroker();

           //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.OnDeviceSubscriptionChanged += DeviceSubscriptionChanged;
           push.OnChannelCreated += ChannelCreated;
           push.OnChannelDestroyed += ChannelDestroyed;

           //------------------------------------------------
           //IMPORTANT NOTE about Push Service Registrations
           //------------------------------------------------
           //Some of the methods in this sample such as 'RegisterAppleServices' depend on you referencing the correct
           //assemblies, and having the correct 'using PushSharp;' in your file since they are extension methods!!!

           // If you don't want to use the extension method helpers you can register a service like this:
           //push.RegisterService<WindowsPhoneToastNotification>(new WindowsPhonePushService());

           //If you register your services like this, you must register the service for each type of notification
           //you want it to handle.  In the case of WindowsPhone, there are several notification types!
           if (DeviceType == "iOS")
           {
               SendAppleNotifications(push, deviceId, message, eventId, eventType, startDate);
           }
           else if (DeviceType == "ANDROID")
           {
               SendAndroidNotifications(push, deviceId, message, eventId, eventType, startDate);
           }
           else if (DeviceType == "WindowsPhone")
           {
               SendWindowsPhoneNotifications(push);
           }
           else if (DeviceType == "Windows")
           {
               SendWindowsNotifications(push);
           }

           //Console.WriteLine("Waiting for Queue to Finish...");

           //Stop and wait for the queues to drains
           push.StopAllServices();

           //Console.WriteLine("Queue Finished, press return to exit...");
           //Console.ReadLine();
       }

       private static void SendAppleNotifications(PushBroker push, string deviceId, string message, long eventId, string eventType, DateTime startDate)
       {
           string certificationPath = ConfigurationManager.AppSettings["CertificationPath"].ToString();
           string certificationPassword = ConfigurationManager.AppSettings["CertificationPassword"].ToString();
           bool isSandbox = Utilities.GetBool(ConfigurationManager.AppSettings["IsSandBox"]);

           //-------------------------
           // APPLE NOTIFICATIONS
           //-------------------------
           //Configure and start Apple APNS
           // IMPORTANT: Make sure you use the right Push certificate.  Apple allows you to generate one for connecting to Sandbox,
           //   and one for connecting to Production.  You must use the right one, to match the provisioning profile you build your
           //   app with!
           var appleCert = File.ReadAllBytes(certificationPath);
           //IMPORTANT: If you are using a Development provisioning Profile, you must use the Sandbox push notification server
           //  (so you would leave the first arg in the ctor of ApplePushChannelSettings as 'false')
           //  If you are using an AdHoc or AppStore provisioning profile, you must use the Production push notification server
           //  (so you would change the first arg in the ctor of ApplePushChannelSettings to 'true')
           push.RegisterAppleService(new ApplePushChannelSettings(!isSandbox, appleCert, certificationPassword)); //Extension method
           //Fluent construction of an iOS notification
           //IMPORTANT: For iOS you MUST MUST MUST use your own DeviceToken here that gets generated within your iOS app itself when the Application Delegate
           //  for registered for remote notifications is called, and the device token is passed back to you
           push.QueueNotification(new AppleNotification()
                                      .ForDeviceToken(deviceId)
                                      .WithAlert(message)
                                      .WithCustomItem("mid", eventId)
                                      .WithCustomItem("mtype", eventType)
                                      .WithBadge(Utilities.GetInt(ConfigurationManager.AppSettings["PayloadBadge"].ToString()))
                                      .WithSound(ConfigurationManager.AppSettings["PayloadSound"].ToString()));
       }

       private static void SendAndroidNotifications(PushBroker push, string deviceId, string message, long eventId, string eventType, DateTime startDate)
       {
           string GoogleAppID = ConfigurationManager.AppSettings["GoogleApplicationAPIKey"].ToString(); // "AIzaSyCuOLQk1Uk7nIwSjNE16OBN_JE183fGnj8";
           var SENDER_ID = ConfigurationManager.AppSettings["SenderId"].ToString();  //"379652277052";
           string GoogleAPIURL = ConfigurationManager.AppSettings["GoogleAPIUrl"].ToString();  //"https://android.googleapis.com/gcm/send";

           //---------------------------
           // ANDROID GCM NOTIFICATIONS
           //---------------------------
           //Configure and start Android GCM
           //IMPORTANT: The API KEY comes from your Google APIs Console App, under the API Access section,
           //  by choosing 'Create new Server key...'
           //  You must ensure the 'Google Cloud Messaging for Android' service is enabled in your APIs Console
           push.RegisterGcmService(new GcmPushChannelSettings(GoogleAppID));
           //Fluent construction of an Android GCM Notification
           //IMPORTANT: For Android you MUST use your own RegistrationId here that gets generated within your Android app itself!
           string json = "{\"collapse_key\": \"demo\",\"time_to_live\": 108,\"delay_while_idle\":true,\"data\":{ \"mulitplemessage\":" + message.ToJSON() + ", \"mid\": \"" + eventId + "\", \"mtype\": \"" + eventType + "\", \"mdate\": \"" + startDate.ToShortDateString() + "\" },  \"registration_ids\":" + deviceId.ToJSON() + "}";
           json = "{\"alert\":" + message.ToJSON() + ",\"badge\":7,\"sound\":\"sound.caf\"}";
           push.QueueNotification(new GcmNotification().ForDeviceRegistrationId(deviceId)
                                 .WithJson(json));
       }

       private static void SendWindowsPhoneNotifications(PushBroker push)
       {
           ////-----------------------------
           //// WINDOWS PHONE NOTIFICATIONS
           ////-----------------------------
           ////Configure and start Windows Phone Notifications
           //push.RegisterWindowsPhoneService();
           ////Fluent construction of a Windows Phone Toast notification
           ////IMPORTANT: For Windows Phone you MUST use your own Endpoint Uri here that gets generated within your Windows Phone app itself!
           //push.QueueNotification(new WindowsPhoneToastNotification()
           //    .ForEndpointUri(new Uri("DEVICE REGISTRATION CHANNEL URI HERE"))
           //    .ForOSVersion(WindowsPhoneDeviceOSVersion.MangoSevenPointFive)
           //    .WithBatchingInterval(BatchingInterval.Immediate)
           //    .WithNavigatePath("/MainPage.xaml")
           //    .WithText1("PushSharp")
           //    .WithText2("This is a Toast"));
       }

       private static void SendWindowsNotifications(PushBroker push)
       {
           ////-------------------------
           //// WINDOWS NOTIFICATIONS
           ////-------------------------
           ////Configure and start Windows Notifications
           //push.RegisterWindowsService(new WindowsPushChannelSettings("WINDOWS APP PACKAGE NAME HERE",
           //    "WINDOWS APP PACKAGE SECURITY IDENTIFIER HERE", "CLIENT SECRET HERE"));
           ////Fluent construction of a Windows Toast Notification
           //push.QueueNotification(new WindowsToastNotification()
           //    .AsToastText01("This is a test")
           //    .ForChannelUri("DEVICE CHANNEL URI HERE"));
       }



       void DeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, INotification notification)
       {
           //Currently this event will only ever happen for Android GCM
           Console.WriteLine("Device Registration Changed:  Old-> " + oldSubscriptionId + "  New-> " + newSubscriptionId + " -> " + notification);
       }

       void NotificationSent(object sender, INotification notification)
       {
           Console.WriteLine("Sent: " + sender + " -> " + notification);
       }

       void NotificationFailed(object sender, INotification notification, Exception notificationFailureException)
       {
           Console.WriteLine("Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification);
       }

       void ChannelException(object sender, IPushChannel channel, Exception exception)
       {
           Console.WriteLine("Channel Exception: " + sender + " -> " + exception);
       }

       void ServiceException(object sender, Exception exception)
       {
           Console.WriteLine("Channel Exception: " + sender + " -> " + exception);
       }

       void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification)
       {
           Console.WriteLine("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId);
       }

       void ChannelDestroyed(object sender)
       {
           Console.WriteLine("Channel Destroyed for: " + sender);
       }

       void ChannelCreated(object sender, IPushChannel pushChannel)
       {
           Console.WriteLine("Channel Created for: " + sender);
       }
   }
}


Step 4:

In Android send the multiple push notification using the below code.


public string SendMultipleNotification(string[] deviceIds, string[] messages, long id, string type, DateTime startDate)

{

string GoogleAppID = ConfigurationManager.AppSettings["GoogleApplicationAPIKey"].ToString(); //"AIzaSyCuOLQk1Uk7nIwSjNE16OBN_JE183fGnj8";

           

var SENDER_ID = ConfigurationManager.AppSettings["SenderId"].ToString();  //"379652277052";

           
string GoogleAPIURL = ConfigurationManager.AppSettings["GoogleAPIUrl"].ToString();  //"https://android.googleapis.com/gcm/send";
           
var value = messages;
           var tRequest = (HttpWebRequest)WebRequest.Create(GoogleAPIURL);
           tRequest.Method = "post";
           tRequest.ContentType = "application/json";
           tRequest.Headers.Add("Authorization: key=" + GoogleAppID);
           tRequest.Headers.Add(string.Format("Sender: id={0}", SENDER_ID));
           String sResponseFromServer = String.Empty;
           string _dateTimeStr = DateTime.Now.ToShortDateString();

string json = "{\"collapse_key\": \"demo\",\"time_to_live\": 108,\"delay_while_idle\":true,\"data\":{ \"mulitplemessage\":" + messages.ToJSON() + ", \"mid\": \"" + id + "\", \"mtype\": \"" + type + "\", \"mdate\": \"" + startDate.ToShortDateString() + "\" },  \"registration_ids\":" + deviceIds.ToJSON() + "}";
           byte[] byteArray = Encoding.UTF8.GetBytes(json);

           // Set the ContentLength property of the WebRequest.
           tRequest.ContentLength = byteArray.Length;

           // Get the request stream.
           using (Stream dataStream = tRequest.GetRequestStream())
           {
               // Write the data to the request stream.
               dataStream.Write(byteArray, 0, byteArray.Length);
           }

           // Get the response.
           WebResponse response = tRequest.GetResponse();

           //Error "The remote server returned an error: (400) Bad Request"
           // Display the status.
           //Console.WriteLine(((HttpWebResponse)response).StatusDescription);

           // Get the stream containing content returned by the server.
           using (Stream dataStream = response.GetResponseStream())
           {
               // Open the stream using a StreamReader for easy access.
               using (var reader = new StreamReader(dataStream))
               {
                   // Read the content.
                   string responseFromServer = reader.ReadToEnd();

                   // Display the content.
                   //Console.WriteLine(responseFromServer);
                   sResponseFromServer = responseFromServer;
               }
           }
           return sResponseFromServer;
       }

In Ios send the multiple push notification using the below code.

  1. Using PushBrokerHandler class method to send the notification.


PushBrokerHandler().SendNotifications(pushNotificationDevice.DOS, pushNotificationDevice.GCMUQId, _notificationMessage, _thisNotificatoin.id, _tempEventType, !string.IsNullOrEmpty(_thisNotificatoin.start) ? Utility.GetDateTime(_thisNotificatoin.start) : _thisNotificatoin.startdate);

                                   _isMessageSend = true;

No comments:

Post a Comment