Aerogear Unified Push Server (UPS) macht es möglich über einen Aufruf Push-Nachrichten an verschiedene Gerätefamilien zu versenden. Dadurch spielt UPS ideal mit Xamarin zusammen. Es können Geräte mit iOS, Android und Windows Betriebssystem registriert werden. Aerogear bietet auch noch andere Push-Nachrichten Dienste, die jedoch für Xamarin keine Rolle spielen.

Leider bietet Aerogear keine Anleitung für Xamarin. Es gibt jedoch ein Modul für c#, aus dem man viel Sourcecode herausziehen und etwas abändern kann, um sich beim UPS zu registrieren.

Andere Blogs zum Thema Aerogear:

Registrieren des Gerätes beim Push-Nachrichten Dienst (APNS, GCM, WNS)

Zunächst ist die Registrierung des Gerätes beim jeweiligen Push Service nötig. Dies kann nach Anleitung von Xamarin erfolgen:

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

https://developer.xamarin.com/guides/cross-platform/application_fundamentals/notifications/android/remote_notifications_in_android/

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):

Haben wir selber noch nicht getestet, aber man sollte der Anleitung von Aerogear folgen können, da diese ja für c# geschrieben wurden:
https://aerogear.org/docs/unifiedpush/aerogear-push-windows/guides/#windows-setup

Auch das Modul für Windows, das wir ja auch als Vorlage verwendet haben, kann über NuGet installiert und verwendet werden:
https://aerogear.org/windows/

(Falls wir in Zukunft Push-Nachrichten für Windows implementieren werden und es hier zu Problemen kommt, wird es ein Update dieses Blog Eintrags geben)

Exemplarisch die Registrierung beim APNS.
In der AppDelegate.cs.
APNS kontaktieren unter 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);
}

Antwort von APNS empfangen und DeviceToken speichern.

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() stößt nun die Registrierung des Geräts beim UPS an.

Hat die Registrierung mit dem jeweiligen Dienst geklappt, liefert dieser einen DeviceToken zurück, den wir dann an Aerogear übergeben müssen.

Registrierung beim Aerogear UPS

Voraussetzung für die Kommunikation mit dem Aerogear Server sind folgende Pakete, die in Xamarin hinzugefügt werden müssen:

DeviceInfo (Xam.Plugin.DeviveInfo)
Json (Newtonsoft.Json)

Wie schon erwähnt übernehmen wir Funktionen auf dem Aerogear Windows Modul und passen diese leicht an. Genauer gesagt geht es um den UPSHttpClient, den man sich hier auf GitHub ansehen kann:
https://github.com/aerogear/aerogear-windows-push/blob/master/aerogear-windows-push/UPSHttpClient.cs

Wir erstellen eine neue C# Klasse mit dem Namen UPSRegister.

Sourcecode der Klasse:

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;
        }
}

Die Klasse sollte weitgehend selbsterklärend sein. Was wir der Klasse hinzugefügt haben, ist die Möglichkeit auch Anfragen an einen Server zu senden, der nur über ein selbstsigniertes SSL Zertifikat verfügt.

Die Funktion Validator bestätigt, dass die Verbindung zum Server vertrauenswürdig ist. In Zeile XX wird diese verwendet:

ServicePointManager.ServerCertificateValidationCallback = Validator;

Ohne diese Ergänzung wird die Registrierung scheitern und das wahrscheinlich ohne eindeutige Fehlermeldung. Hat man ein bestätigtes SSL Zertifikat auf dem Server, dann sind diese Ergänzung unnötig und können herausgenommen werden.

Jetzt können wir die neue Klasse instanziieren und die Registrierung anstoßen. Hierfür gehen wir zurück in die AppDelegate.cs für iOS bzw. RegistrationIntentService.cs (siehe Xamarin GCM Anleitung) für Android und implementieren die Methode RegisterUPS(). Diese haben wir ja schon in RegisteredForRemoteNotifications im iOS Beispiel verwendet.

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);
}

Unter Android wird RegisterUPS in der Methode SendRegistrationToAppServer (string token) aufgerufen.

In den Zeilen 4-6 müssen die Platzhalter mit den Informationen aus Aerogear im der jeweiligen Variant zum benutzten OS ersetzt werden.

Hat alles geklappt, kann man sein Gerät nun bei Aerogear registrieren und es ist bereit, Push-Nachrichten zu empfangen.

Empfangen von Push-Nachrichten

Das Empfangen von Push-Nachrichten kann wieder analog der jeweiligen Anleitungen von Xamarin(iOS, Andoid) und Aerogear (Windows) erfolgen.

Beispiel iOS in AppDelegate.cs:

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

Die Methode ProcessNotifocation kann auf verschiedene Weisen implementiert werden.

Ein Beispiel:

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();
            }
        }
    }
}

Die eigentliche Nachricht liegt verschachtelt im NSDictionary options. Unter dem Schlüssel aps gibt es noch einmal ein NSDictionary das wiederum unter dem Schlüssel alert ein weiteres NSDictionary enthält. Dort liegt nun die Nachricht unter dem Schlüssel body.

Die Nachricht kann nun nach Wunsch verarbeitet werden. Beispielsweise als Alert ausgegeben, wie es im Beispiel geschieht.