With the  Aerogear Unified Push Server (UPS) it is possible to send Push-Notifications to various Device-Families. It is possible to register devices with iOS, Android and Windows Operating Systems.

Unfortunately there is no Guide for Xamarin. But there is a module for c# from which you can adapt a lot of source code to make the registration with UPs possible.

Device registration at the Push-Notification Service (APNS, GCM, WNS)

First you need to register the device at the Push-Notification Service. Therefore you can follow the instructions from Xamarin or Aerogear:

APNS (Apple Push Notification Service):
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/notifications/ios/remote_notifications_in_ios/

GCM (Goolge Cloud Messaging), inkl. Kapitel „Register with GCM“:
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/notifications/android/google-cloud-messaging/

WNS (Windows Push Notification Service):

We haven’t tests WNS yet. but it should be possible to follow the instructions from Aerogear for C#:
https://aerogear.org/docs/unifiedpush/aerogear-push-windows/guides/#windows-setup

Also the Aerogar module for Windows (C#) should work fine. It can be installed easily via NuGet:
https://aerogear.org/windows/

Here is an example how to register with APNS.
In AppDelegate.cs.
Connect to APNS in FinishedLaunching:

//register for apns
if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
    var pushSettings = UIUserNotificationSettings.GetSettingsForTypes (
            UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
            new NSSet ());
    
        UIApplication.SharedApplication.RegisterUserNotificationSettings (pushSettings);
        UIApplication.SharedApplication.RegisterForRemoteNotifications ();
    } else {
        UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
        UIApplication.SharedApplication.RegisterForRemoteNotificationTypes (notificationTypes);
}

Receive Answer (DeviceToken) from APNS and save the DeviceToken.

public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
        // Get current device token
        var DeviceToken = deviceToken.Description;
        if (!string.IsNullOrWhiteSpace(DeviceToken)) {
            DeviceToken = DeviceToken.Trim('<').Trim('>');
        }

        // Get previous device token
        var oldDeviceToken = NSUserDefaults.StandardUserDefaults.StringForKey("PushDeviceToken");

        // Has the token changed?
        if (string.IsNullOrEmpty(oldDeviceToken) || !oldDeviceToken.Equals(DeviceToken))
        {
            //Call function to register device with UPS
            RegisterUPS(DeviceToken);
        }

        // Save new device token 
        NSUserDefaults.StandardUserDefaults.SetString(DeviceToken, "PushDeviceToken");
}

RegisterUPS() will then register the Device with UPS.

If the registration succeeded, APNS will return a DeviceToken which we need to send to UPS for registration.

Registration with UPS

As mentioned we adopted code from the Aerogear Windows module. In particular we used the UPSHttpClient, you can find the original code here:
https://github.com/aerogear/aerogear-windows-push/blob/master/aerogear-windows-push/UPSHttpClient.cs

We created a new C# class named UPSRegister.

Source code of UPSRegister:

public sealed class UPSRegister //: AeroGear.Push.IUPSHttpClient
{

        private const string AuthorizationHeader = "Authorization";
        private const string AuthorizationMethod = "Basic";
        private const string RegistrationEndpoint = "rest/registry/device";
        private const string MetricEndpoint = RegistrationEndpoint + "/pushMessage/";

        public UPSRegister(Uri uri, string username, string password)
        {
            Uri = uri;
            Username = username;
            Password = password;
        }

        private Uri Uri { get; set; }
        private string Username { get; set; }
        private string Password { get; set; }

        public async Task Register(Installation installation)
        {
            var request = CreateRequest(CreateEndpoint(RegistrationEndpoint));
            request.Method = "POST";

            string json = Newtonsoft.Json.JsonConvert.SerializeObject (installation);

            var stream = await request.GetRequestStreamAsync ();
            using (var writer = new StreamWriter (stream)) {
                writer.Write (json);
                writer.Flush ();
                writer.Dispose ();
            }

            ServicePointManager.ServerCertificateValidationCallback = Validator;
            var responseObject =
                (HttpWebResponse)
                await Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, request);
            await new StreamReader(responseObject.GetResponseStream()).ReadToEndAsync();
            return responseObject.StatusCode;
        }

        public string CreateEndpoint(string path)
        {
            return (Uri.ToString().EndsWith("/") ? Uri.ToString() : Uri + "/") + path;
        }

        private HttpWebRequest CreateRequest(string endpoint)
        {
            var request = (HttpWebRequest) WebRequest.Create(endpoint);
            request.ContentType = "application/json";
            request.Headers[AuthorizationHeader] = AuthorizationMethod + " " + CreateHash(Username, Password);
            return request;
        }

        private static string CreateHash(string username, string password)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password));
        }

        public static bool Validator (object sender, X509Certificate certificate, X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
}

This class should be self explaining. The most im portend thing we added is the possibility to use a self signed SSL certificate on our server.

The function Validator validates that the connection is trustworthy. It is used in row 34:

ServicePointManager.ServerCertificateValidationCallback = Validator;

Without this addition the connection will be rejected and probably without an error message. If you have bought a valid certificate you don’t need this addition.

Now we can instantiate this class and start the registration. We can do this in AppDelegate.cs for iOS or RegistrationIntentService.cs (see GCM Guide) for Android. Then we have to implement RegisterUPS().

RegisterUPS sourcecode:

public void RegisterUPS(string DeviceToken){

        //register
        var UnifiedPushUri = new Uri (SERVER_URL);
        var VariantId = VARIANT_ID;
        var VariantSecret = VARIANT_SECRET;

        Installation ins = new Installation ();
        ins.deviceToken = DeviceToken;
        ins.alias = "";
        ins.platform = DeviceInfo.Plugin.CrossDeviceInfo.Current.Model;
        ins.osVersion = DeviceInfo.Plugin.CrossDeviceInfo.Current.Version;
        ins.operatingSystem = DeviceInfo.Plugin.CrossDeviceInfo.Current.Platform.ToString();


        UPSRegister client = new UPSRegister (UnifiedPushUri, VariantId, VariantSecret);
        client.Register(ins);
}

If you are implementing for Android RegisterUPS is called in SendRegistrationToAppServer (string token).

You have to change the placeholders in  rows 4-6 with the information you get in Aerogear in the variant for your OS.

If everything went fine, you can now register your device with Aerogear and start sending push-notifications.

Receiving Push-Notifications

You can follow the instructions in the corresponding guides für Xamerin (iOS, Android) and Aerogear (Windows) on how to implement receiving push-notifications.

Example for iOS in AppDelegate.cs:

public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
      ProcessNotification(userInfo);
}

ProcessNotifocation can be implemented in different ways.

Our example:

void ProcessNotification(NSDictionary options)
{
    Console.WriteLine ("received notification");
    // Check to see if the dictionary has the aps key.  This is the notification payload you would have sent
    if (null != options && options.ContainsKey(new NSString("aps")))
    {
        //Get the aps dictionary
        NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;

        string body = string.Empty;

        //Extract the alert text
        if (aps.ContainsKey (new NSString ("alert"))) {
            NSDictionary alert = aps.ObjectForKey (new NSString("alert")) as NSDictionary;
            body = (alert [new NSString ("body")] as NSString).ToString ();
        }

            //Manually show an alert
            if (!string.IsNullOrEmpty(body))
            {
                UIAlertView avAlert = new UIAlertView("Message", body, null, "OK", null);
                avAlert.Show();
            }
        }
    }
}

The message sent is located in the NSDictionary options. Under the key als is another NSDictionary that contains another  NSDictionary with the key alert. The message is located in this dictionary under the key body.

This message can now be processed as you wish.  In our example we create an Alert dialog to display it.