Tag: tutorial

[教學] Apple Push Notification (APN) Service 訊息推播

事前準備

  • iPhone 或是 iPad。推播功能無法在模擬器上使用。
  • iOS 開發者賬號。需要新增 App ID 以及建立相關憑證。
  • 推播 Server。需要一個推播 Server 發送推播訊息。

推播訊息格式

APN 是使用 json 作為傳遞的格式

{
   "aps":
   {
      "alert":"訊息內容",
      "sound":"default",    
      "badge":1
    }
 }

訊息內容(payload)有256 bytes 的長度限制,所以在傳遞時最好把空白字元省略。

{"aps":{"alert":"my message","sound":"default","badge"=1}}

詳信內容可參考 Push Notificaiton Programming Guide

產生憑證 (certificate)

透過下面的步驟你將會得到一系列的檔案

  1. MyPush.certSigningRequest
  2. MyPushKey.p12
  3. aps_development.cer
  4. MyPushCert.pem
  5. MyPushKey.pem
  6. ck.pem (這是我們最後真正會用到的檔案)

1. 產生 Certificate Signing Request (CSR)

到 iOS Provision Protal 申請憑證會需要 CSR

產生步驟:

1. 在你的 mac 電腦內開啓 Keychain Access,然後從選單內選擇 Request a Certificate From a Certificate Authority

2.在下一步的畫面內填寫email 以及 name,記得要選擇 Save to disk,然後就會產生出 MyPush.certSigningRequest 的檔案

3. 匯出 private key

回到 KeyChain Access 的主畫面,在右上方搜尋框內輸入 “MyPush” 應該會看到剛剛建立的key。然後選擇 private key -> Export My Push。將檔案存成 MyPushKey.p12 以及設定密碼 (passphrase)

2. 產生 SSL certificate

這裡需要登入 iOS Provisioning Portal。我將會省略申請 APP ID 的步驟。

1. Enable for Apple Push Notification

進入 App ID 的設定畫面 (Configure App ID),點選 Enable for Apple Push Notification。你會看到兩個憑證

Development Push SSL Certificate – 在開發過程中使用這組

Production Push SSL Cerificate – 上架時使用這組

(憑證有效期限為一年,記得要重新申請,不然會失效。)

我們這次先設定 Development Push SSL Certificate

2. 下載 SSL Certificate

Enable For Apple Push Notification Service 後,點選 Configure。他會要求你提供前面產生的 CSR 檔案。

將下載下來的檔案存成 aps_development.cer

3. 產生 CK.PEM 檔案

打開 Terminal,進入剛所產生檔案的位置

1. 將 .cer 轉換成 .pem

openssl x509 -in aps_development.cer -inform der -out MyPushCert.pem

2. 將 .p12 轉換成 .pem,這邊會要求你輸入之前所設定的密碼。

$ openssl pkcs12 -nocerts -out MyPushKey.pem -in MyPushtKey.p12
Enter Import Password: 
MAC verified OK
Enter PEM pass phrase: 
Verifying - Enter PEM pass phrase:

3. 最後將兩個 pem 檔案合併

cat MyPushCert.pem MyPushKey.pem > ck.pem

APP 端設定 (更新 iOS 8)

修改 MyPushAppDelegate.m

註冊  push notification

在 didFinishLaunchingWithOptions 內加入下面程式碼

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
   self.window.rootViewController = self.viewController;
   [self.window makeKeyAndVisible]; 
   ...
   [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
		(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
   return YES; 
}

////////////////////////////////////////////////////////////
/// iOS 8.0 更新
////////////////////////////////////////////////////////////

if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)])
{
   [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
   [[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
   [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeNewsstandContentAvailability];
}

接收 device token

在 didRegisterForRemoteNotificationsWithDeviceToken 內加入下面程式碼

這裡也是很好的時機點將所收到的 device token 資料傳到 APN Server

- (void)application:(UIApplication*)application
         didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
 
    NSString *tokenStr = [deviceToken description];
    NSString *pushToken = [[[tokenStr 
      stringByReplacingOccurrencesOfString:@"" withString:@""] 
      stringByReplacingOccurrencesOfString:@" " withString:@""];
 
   // 將所收到的 device token 資料傳到 APN Server
}

「註」在 APP 開啓的情況下,是不會收到APN的,所以記得測試的時候要先將 APP 關閉

Server端範例

<?php
 
// 要發送裝置的 device token
$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';
 
// 私鑰的密碼
$passphrase = 'password';
 
$message = 'Hello World!';
 
////////////////////////////////////////////////////////////////////////////////
 
$ctx = stream_context_create();
// 帶入 ck.pem
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
 
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', 
      $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
 
$body['aps'] = array('alert' => $message, 'sound' => 'default');
$payload = json_encode($body);
$msg = chr(0).pack('n', 32).pack('H*', $deviceToken).pack('n', strlen($payload)).$payload;
// 送出訊息
fwrite($fp, $msg, strlen($msg));
fclose($fp);
?>
Tags : , , , ,