XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2019/05/27

使用 PushSharp 來推送 Push Notification 到 Xamarin.Forms 開發的 Android 或者 iOS App 上

使用 PushSharp 來推送 Push Notification 到 Xamarin.Forms 開發的 Android 或者 iOS App 上

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式


最近在幫客戶處理 Xamarin.Forms 推播 Push Notification 需求的時候,產生了一個問題,那就是原先的軟體都可以正常收到來自 Apple APNS 的推播訊息,突然間,客戶向我反映,現在正在開發的測試版本 App,突然間收不到來自後台伺服器推送出來的推播訊息,詢問我是不是我這裡程式做了甚麼修正,導致這樣的問題。由於整個系統規劃架構是,當 App 第一次啟動的時候,從來自遠端 PNS 取得了該裝置的 Token,會於使用者登入系統的時候,將此 Token 傳送到遠端 Web API 服務,接著,後端的 Web API 服務,則會將此 Token 再向 Azure Notification Hub 進行註冊;而當有狀況需要推送到 App 的時候,後端 API 服務需要呼叫 Azure Notification Hub 的 SDK,請求 Azure Notification Hub 幫忙向 Apple APNS 推送出這個訊息的推播內容。
因此,首先就透過 Azure Notification Hub 的測試推播功能,發送出一個該 App 使用者可以接收的推播訊息,可是,該 App 還是沒有收到任何推播內容,最後,是在客戶的幫忙,將 App 取得的推播 Token,使用他寫的程式,直接向 Apple APNS 發送推播通知請求,結果,我開發的 App 是可以接收到這個推播通知,所以,可以證明 Xamarin.Forms App 對於來自遠端 Apple APNS 推播內容,是可以正常運作的。
經過這次的經驗,我覺得我這裡還是需要有個這樣的直接對於 Google / Apple 的 PNS 系統,直接送出推播通知請求,以便日後方便針對這樣的問題進行除錯。
要完成這樣的需求,首先需要安裝 PushSharp 的 NuGet 套件,這裡將會建立一個 Console App,完成底下的測試推播程式碼(在該篇文章的最後面)。
在這裡的程式碼將會由 PushSharp 所提供的範例說明程式碼,整理出可以對於 Android / iOS App 推送出 Push Notification 的程式碼,因此,想要對 iOS App 推送出一個推播內容,就可以呼叫 PushiOS 這個方法,而需要將該 App 取得的推播 Token,傳送進去,若想要對 Android App 推送出一個推播內容,就可以呼叫 PushAndroid 這個方法,當然,這裡也需要將該 App 取得的推播 Firebase 的 Token,傳送進去。

針對 iOS 裝置進行訊息推播

在 PushiOS 方法中,這個敘述 string sendMessage = File.ReadAllText("iOS.json", System.Text.Encoding.UTF8); 將會從這個專案目錄下,讀取出要發送出去的推播內容,這裡是一個 JSON 格式內容,如下所示:
C Sharp / C#
{
  "aps": {
    "alert": {
      "title": "推播主題",
      "body": "Notification Hub test notification"
    },
    "sound": "default"
  }
}
透過 apnsBroker.QueueNotification(new ApnsNotification{DeviceToken = deviceid, Payload = JObject.Parse(sendMessage)}); 方法呼叫,可以將指定 App 推播 Token ,也就是 deviceid 這個參數,與上述指定的推播內容,傳送到 Apple APNS ( Apple Push Notification Service ) 上,進而 iOS App 就會收到這個推播通知。
在建立可以用於 Apple APNS 推播的物件時候,會需要使用到 var config = new ApnsConfiguration(ApnsConfiguration.ApnsServerEnvironment.Sandbox, Constants.APNS_P12FILENAME, Constants.PUSH_CERT_PWD); 敘述,在這裡的第一個參數使用了 ApnsConfiguration.ApnsServerEnvironment.Sandbox ,表示使用的是開發模式下的推播,因此,需要從 Apple Developer 蘋果開發人員網頁中,點選設定的 App ID,下載出該推播憑證檔案,轉換成為 .p12 憑證格式;在這裡是將這個憑證檔案放入測試專案內,指定 輸出到目錄屬性為一律複製,因此,第二個引述將會指向這個檔案所在的路徑,而第三個引數則是要使用這個憑證的時候,需要使用到的密碼。
在這個測試專案中,對於 Android.json (Android 推播內容), iOS.json (iOS 推播內容) 與 com.vulcan.azurehub.p12 (iOS 推播憑證) 都已經放置到該專案內。
想要進行 iOS 的推播測試,需要使用實體裝置,例如,這裡使用的一台 iPhone 手機 (不像 Android 平台,可以使用模擬器來進行測試,只要該模擬器上有安裝 Google Play 軟體即可),底下為進行 iOS 裝置下的推送測試結果,這個範例程式是可以送出讓 iOS App 上收到該推播通知。
在底下,測試了當送出推播訊息之後,App 正好在前景狀態與在背景狀態情境下,所看到收到推播訊息的畫面。

針對 Android 裝置進行訊息推播

在 PushAndroid 方法中,這個敘述 string sendMessage = File.ReadAllText("Android.json", System.Text.Encoding.UTF8); 將會從這個專案目錄下,讀取出要發送出去的推播內容,這裡是一個 JSON 格式內容,如下所示:
C Sharp / C#
{
  "data": {
    "title": "推播主題",
    "message": "Notification Hub test notification"
  }
}
透過 gcmBroker.QueueNotification(new GcmNotification{ RegistrationIds = new List<string> { regId }, Data = JObject.Parse(sendMessage) }); 方法呼叫,可以將指定 App 推播 Token ,也就是 regId 這個參數,與上述指定的推播內容,傳送到 FCM ( Firebase Cloud Messaging ) 上,進而 Google App 就會收到這個推播通知。
在建立可以用於 Apple APNS 推播的物件時候,會需要使用到 var config = new GcmConfiguration(Constants.GCM_SENDER_ID, Constants.AUTH_TOKEN, null); 敘述,第一個引數,GCM_SEND_ID 與第二個引數, AUTH_TOKEN 可以從 Firebase Console 網站中取得。
底下為進行 Android 裝置下的推送測試結果,這個範例程式是可以送出讓 Android App 上收到該推播通知。

發送 iOS / Android 推播的測試程式原始碼

C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        // 進行 iOS 裝置的推播
        PushiOS("ad1942af16e2d85d40fbc7e186555eb276ce67a8112fe5fbe0b6b33a1d404a3f");
        // 進行 Android 裝置的推播
        //PushAndroid("eJTHqO53Tbk:APA91bGUWCleYTdY39Ws1JmbtMnJm80RRsWm8sE5zINsd5YP4dDi_lqp1MHnD38Hr-x7UpIdx0VP3TEuOgpQ7W77bE_c0k4G2FGLK73GAiHsdqEJZhyF8rBIjC7hmfBHOoqteE_cWCbC");
    }

    static void PushiOS(string deviceid)
    {
        // Configuration (NOTE: .pfx can also be used here)
        var config = new ApnsConfiguration(ApnsConfiguration.ApnsServerEnvironment.Sandbox,
            Constants.APNS_P12FILENAME, Constants.PUSH_CERT_PWD);

        // Create a new broker
        var apnsBroker = new ApnsServiceBroker(config);

        // Wire up events
        apnsBroker.OnNotificationFailed += (notification, aggregateEx) =>
        {

            aggregateEx.Handle(ex =>
            {

                // See what kind of exception it was to further diagnose
                if (ex is ApnsNotificationException notificationException)
                {

                    // Deal with the failed notification
                    var apnsNotification = notificationException.Notification;
                    var statusCode = notificationException.ErrorStatusCode;

                    Console.WriteLine($"Apple Notification Failed: ID={apnsNotification.Identifier}, Code={statusCode}");

                }
                else
                {
                    // Inner exception might hold more useful information like an ApnsConnectionException            
                    Console.WriteLine($"Apple Notification Failed for some unknown reason : {ex.InnerException}");
                }

                // Mark it as handled
                return true;
            });
        };

        apnsBroker.OnNotificationSucceeded += (notification) =>
        {
            Console.WriteLine("Apple Notification Sent!");
        };

        // Start the broker
        apnsBroker.Start();

        string sendMessage = File.ReadAllText("iOS.json", System.Text.Encoding.UTF8);

        // Queue a notification to send
        apnsBroker.QueueNotification(new ApnsNotification
        {
            DeviceToken = deviceid,
            Payload = JObject.Parse(sendMessage)
        });

        // Stop the broker, wait for it to finish   
        // This isn't done after every message, but after you're
        // done with the broker
        apnsBroker.Stop();
    }

    static void PushAndroid(string regId)
    {
        // Configuration GCM (use this section for GCM)
        var config = new GcmConfiguration(Constants.GCM_SENDER_ID, Constants.AUTH_TOKEN, null);
        var provider = "GCM";

        // Configuration FCM (use this section for FCM)
        // var config = new GcmConfiguration("APIKEY");
        // config.GcmUrl = "https://fcm.googleapis.com/fcm/send";
        // var provider = "FCM";

        // Create a new broker
        var gcmBroker = new GcmServiceBroker(config);

        // Wire up events
        gcmBroker.OnNotificationFailed += (notification, aggregateEx) =>
        {

            aggregateEx.Handle(ex =>
            {

                // See what kind of exception it was to further diagnose
                if (ex is GcmNotificationException notificationException)
                {

                    // Deal with the failed notification
                    var gcmNotification = notificationException.Notification;
                    var description = notificationException.Description;

                    Console.WriteLine($"{provider} Notification Failed: ID={gcmNotification.MessageId}, Desc={description}");
                }
                else if (ex is GcmMulticastResultException multicastException)
                {

                    foreach (var succeededNotification in multicastException.Succeeded)
                    {
                        Console.WriteLine($"{provider} Notification Succeeded: ID={succeededNotification.MessageId}");
                    }

                    foreach (var failedKvp in multicastException.Failed)
                    {
                        var n = failedKvp.Key;
                        var e = failedKvp.Value;

                        Console.WriteLine($"{provider} Notification Failed: ID={n.MessageId}, Desc={e.Message}");
                    }

                }
                else if (ex is DeviceSubscriptionExpiredException expiredException)
                {

                    var oldId = expiredException.OldSubscriptionId;
                    var newId = expiredException.NewSubscriptionId;

                    Console.WriteLine($"Device RegistrationId Expired: {oldId}");

                    if (!string.IsNullOrWhiteSpace(newId))
                    {
                        // If this value isn't null, our subscription changed and we should update our database
                        Console.WriteLine($"Device RegistrationId Changed To: {newId}");
                    }
                }
                else if (ex is RetryAfterException retryException)
                {

                    // If you get rate limited, you should stop sending messages until after the RetryAfterUtc date
                    Console.WriteLine($"{provider} Rate Limited, don't send more until after {retryException.RetryAfterUtc}");
                }
                else
                {
                    Console.WriteLine("{provider} Notification Failed for some unknown reason");
                }

                // Mark it as handled
                return true;
            });
        };

        gcmBroker.OnNotificationSucceeded += (notification) =>
        {
            Console.WriteLine("{provider} Notification Sent!");
        };

        // Start the broker
        gcmBroker.Start();

        string sendMessage = File.ReadAllText("Android.json", System.Text.Encoding.UTF8);

        // Queue a notification to send
        gcmBroker.QueueNotification(new GcmNotification
        {
            RegistrationIds = new List<string> { regId },
            Data = JObject.Parse(sendMessage)
        });

        // Stop the broker, wait for it to finish   
        // This isn't done after every message, but after you're
        // done with the broker
        gcmBroker.Stop();
    }
}

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式




沒有留言:

張貼留言