使用 PushSharp 來推送 Push Notification 到 Xamarin.Forms 開發的 Android 或者 iOS App 上
因此,首先就透過 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 格式內容,如下所示:
{
  "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 格式內容,如下所示:
{
  "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 推播的測試程式原始碼

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 快速入門] 的使用方式
 
 
 
最近在幫客戶處理 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 推送出這個訊息的推播內容。