2014年3月27日

在iOS應用程式中使用推播通知(Push Notification)功能 - Provider以Java-apns實作

前言

開發iOS應用程式時,我們常常需要連上自己的平台或伺服器建立連線,取得資料。然而,當應用程式尚未開啟,或是已經被退到背景時,此時就無法讓使用者即時看到自己的伺服器送來的新資料了。
推播通知(Apple Push Notification Service, APNs)是當iOS應用程式不在前景時,使自己的平台或伺服器可以發送通知給iOS設備的一項技術。

以下是發送一則推播通知的流程示意圖:
Provider是iOS應用程式開發者自己所擁有的平台或伺服器,APNs Server則是Apple官方的推播通知服務伺服器。 Provider將推播通知的相關資訊依照規定的格式送給APNs Server,APNs Server即可依此資訊將通知傳送給指定的iOS設備。
因此,Provider需要準備以下幾項資料:
  1. APNs SSL Certificate(APNs SSL憑證檔):由於Provider發送推播通知訊息給APNs Server處理時需要進行SSL連線,進行此連線需要準備憑證檔。
  2. Device Token:識別iOS設備的唯一ID,表示需要送給哪一台iOS設備。

Provider事前準備

在App ID設定中啟用推播通知,並建立APNs SSL憑證檔

當某個iOS應用程式需要使用推播通知時,需要先在其App ID設定中啟用此功能。
首先,登入iOS Dev Center, 點選App IDs, 展開該iOS應用程式,進行編輯。
點選編輯後,找到Push Notification項目,點選建立憑證檔(Create Certificate)。
APNs SSL憑證檔分為兩種:開發階段用的Development SSL Certificate與上架之後用的Production SSL Certificate。試此時你的情況與需求使用。
建立APNs SSL憑證檔,需要上傳CSR檔案(CertificateSigningRequest.certSigningRequest)。
完成後,點選下載即可。
下載憑證檔號,點擊兩下憑證檔,開啟鑰匙圈存取(keychain acess),右鍵點選憑證檔,選擇輸出



至此,Provider需要的APNs SSL憑證檔已完成。
注意:編輯完App ID後,該App的Profitioning file需要重新產生。

取得Device Token

取得Device Token需要透過iOS設備來進行。
iOS設備使用UIApplication的registerForRemoteNotificationTypes方法, 來註冊使用推播通知, 並取得Device Token,範例如下:
        
    //注意registerForRemoteNotificationType方法在iOS 8之後已經被宣告為deprecated了
    //因此需要針對不同版本的iOS來執行不同的程式

    UIApplication* application = [UIApplication sharedApplication];
    //若iOS為較新的版本(支援registerUserNotificationSettings方法)
    if([application respondsToSelector:@selector(registerUserNotificationSettings:)]){
        UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes categories:nil];
        [application registerUserNotificationSettings:settings];
        [application registerForRemoteNotifications];
    }
    //若iOS為較舊的版本
    else {
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert| UIRemoteNotificationTypeBadge |  UIRemoteNotificationTypeSound)];
    }
此方法接收一個UIRemoteNotificationType參數,用以設定是否接收Badge, 使用提示聲音。
而在AppDelegate類別(專案中實作UIApplicationDelegate的類別)中,加入下列兩個方法,用以處理註冊成功或失敗
- (void)application:(UIApplication *)app  didRegisterForRemoteNotificationsWithDeviceToken: (NSData *)deviceToken
{
    //將Device Token由NSData轉換為字串     
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *iOSDeviceToken = 
    [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", 
        ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
        ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
        ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];

    //將Device Token傳給Provider...
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError: (NSError *)err {
    //錯誤處理...
}
iOS設備取得Device Token後,iOS應用程式開發者再以任何方式(如:Web Service)將Device Token傳至Provider即可。

由Provider發送推播通知的相關資訊給APNs Server

先前提及,Provider需要將推播通知的相關資訊依照規定的格式送給APNs Server,此相關資訊稱為Notification Payload,其格式為JSON。範例如下:
{
    "aps": {
        "alert": "Alert Message",
        "badge": 3,
        "sound": "alarm"
    },
    "custom_key": "custom_value"
}
若使用開發中的憑證檔(Development SSL Certificate),則Payload要發給測試用的APSs Server,位址是:gateway.sandbox.push.apple.com,port為2195。
若使用上架用的憑證檔(production SSL Certificate),則Payload要發給正式用的APSs Server,位址是:gateway.push.apple.com,port為2195。
而如今,網路上也已經有眾多第三方程式實作Provider的範例可供參考。本文將以Java-apns為例。

使用Java-apns套件實作Provider

java-apns是以Java寫成的Provider套件,只要是可以安裝Java Virtual Machine的環境下都可以執行。
建立ApnsService物件,此時需要提供p12檔案的路徑,以及先前儲存p12檔案時,所使用的密碼。
然後,如果使用Development SSL憑證檔,請使用withSandboxDestination方法; 如果使用Production SSL憑證檔,請使用或是withProductionDestination方法。 如下範例:
String certPath = "<p12 File Path>";
String pwd = "<p12 File password>";
ApnsService service = APNS.newService()
    .withCert(certPath, KokolaConstants.getApnsPwd())
    .withSandboxDestination()   // 或是withProductionDestination(), for 上架用
    .build();
發送一個簡單的通知:
String payload = APNS.newPayload()
    .alertBody("Hello")
    .badge(badge).sound ("alert.wav").build();
service.push(id, payload);
更詳細的內容可以參考Notification Payload,Java-apns幾乎都有實作對應的Method可以使用。

結語

推播通知是個在行動裝置上廣為使用的基本功能,尤其在背景執行程式限制重重的iOS環境下,善用推播通知更是重要。 在iOS下實作推播通知,只要對APNs Server發送正確格式的資料(Notification Payload)即可,因此網路上不難找到各種程式語言/平台實作的Provider,本篇文章僅以Java作為範例,供大家參考。

Reference: