iOS客戶端如何測試推送
當我們在客戶端實現推送跳轉需求後,但是伺服器還沒有做好後臺的配置,此時需要測試收到推送訊息之後能否按照預期,執行我們的程式碼。但在這個情況下如何去做呢。這裡可以提供給大家兩個選擇。
1. 封裝好處理邏輯的介面,在程式啟動或者某個時刻模擬資料,直接呼叫處理邏輯的介面,檢視是否符合預期。該情況只適用於測試跳轉邏輯,不能測試資料解析的正確性(例如欄位名是否正確、欄位的型別是否正確等)。
例如,我們處理推送訊息的函式都寫在了Appdelegate.m中的- (void)dealWithNotification:(NSDictionary*)userInfo函式裡
那麼我們就需要來構造資料進行模擬推送,比如這樣:
// 用於模擬器測試推送 方便快速測試推送問題 使用這個函式測試完,請務必在真實推送情況下測試
- (void)sendNotificationSelf:(RemoteNotificationType)type
{
NSDictionary * dictionary = nil;
if (type == kRemoteNotificationExpression)
dictionary = [NSDictionarydictionaryWithObjectsAndKeys:@(3), @"flag",
elseif (type == kRemoteNotificationDwaft)
dictionary = [NSDictionarydictionaryWithObjectsAndKeys:@(5), @"flag", @"寶貝計劃", @"templateName", @(126), @"templateId", nil];
elseif (type == kRemoteNotificationCharacter)
dictionary = [NSDictionary
elseif (type == kRemoteNotificationSoundExpressionCategory)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(8), @"flag", @(176), @"classifyId", @"", @"classifyName", nil];
elseif (type == kRemoteNotificationSoundExpressionSubjectDetail)
dictionary = [NSDictionarydictionaryWithObjectsAndKeys:@(9), @"flag", @(18), @"subjectId", @"動物成精了", @"subjectName", nil];
elseif (type == kRemoteNotificationMessageCenter)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(10), @"flag", nil];
elseif (type == kRemoteNotificationFansList)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(11), @"flag", nil];
elseif (type == kRemoteNotificationStatusDetail)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(12), @"flag", @"" , @"sid", nil];
elseif (type == kRemoteNotificationUserHomePage)
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@(13), @"flag", [UserInfo getSelf].uid, @"uid", nil];
[selfdealWithNotification:dictionary];
}
這段程式碼的作用非常簡單,其實就是根據預設好的推送型別,來模擬不同的推送資料,進而呼叫推送函式的統一處理方法。
我們可以在應用啟動的時候呼叫來模擬應用未開啟時點選推送的效果,在點選某個按鈕時呼叫來模擬應用在前臺收到推送 的效果,寫一個延時來呼叫然後點選home鍵,來模擬應用在後臺收到推送的效果了。
怎麼樣,看起來還不錯吧,基本上可以解決問題,那還有沒有更好的方案了,請繼續看。
2. 既然伺服器還沒有做好相關的配置,那麼為什麼我們自己做一個閹割版的伺服器,只做推送測試呢?OK,那就瞭解一下,iOS推送通知的基本原理。
iOS推送通知的基本原理
蘋果的推送服務是由自己專門的推送伺服器APNs(Apple Push Notification service)來完成的,過程是APNs收到我們自己的應用伺服器發出的被推送的訊息,將這條訊息推送到制定的iOS裝置上,然後再由iOS裝置通知到我們的應用程式,我們將會以通知或者聲音的形式收到推送回來的訊息。iOS遠端推送的前提是,裝有我們應用程式的iOS裝置,要向APNs伺服器註冊,註冊成功後,APNs伺服器將會給我們返回一個deviceToken,我們獲取到這個token後,將它傳送給我們自己的應用伺服器。當我們需要推送訊息時,我們的應用伺服器將訊息按照指定的格式進行打包,然後結合iOS裝置的deviceToken一起發給APNs伺服器。iOS裝置會和APNs伺服器維持一個基於TCP的長連線,APNs伺服器將新訊息推送到iOS裝置上,然後在裝置螢幕上進行顯示。
其實總結下來過程如下:
1. 客戶端向APNs申請註冊訊息推送服務。
2. APNs伺服器接受到請求後,並將deviceToken返回給裝置上的客戶端
3. 客戶端將deviceToken傳送給自己的後臺伺服器,後臺接受後並儲存。
4. 後臺伺服器向APNs伺服器傳送推送訊息
5. APNs伺服器接受後,將訊息傳送給deviceToken對應的裝置
我們來看上面的過程中伺服器要做的就是接受並儲存客戶端傳送的deviceToken,以便之後再要傳送推送時,與APNs傳送推送訊息。所以如果要哦讓我們自己做一個閹割版的服務端也很簡單,首先deviceToken,只要我們不解除安裝應用,那麼他是不會變化的。所以我們只要在XCode中輸出deviceToken並且記錄下來就可以解決儲存的問題,那麼後臺伺服器怎麼與APNs伺服器互動的呢,其實只需要跟APNs進行SSL連線,把我們APNs預期的格式的資料傳遞過去就可以了。基於Mac平臺,我們可以選擇PHP來實現,畢竟Mac自帶了PHP環境,使用起來很方便(況且PHP是世界上最好的語言J)。
使用PHP進行通訊的話,就需要使用.pem檔案。
.PEM檔案是什麼?
如果把SSL系統比喻成工商局系統。
首先有SSL就有CA,certificateauthority。證書局,同於製作、認證證書的第三方機構,我們假設應用執照非常難製作,就像身份證一樣,需要有製作公司來提供,並且提供技術幫助工商局驗證執照的真偽。
然後CA是可以有很多的,也就是可以有多個制證公司,但是工商局只有一個,它來說哪個制證公司是可信的,哪些是假的,需要打擊。在SSL的世界中,微軟、谷歌和Mozilla扮演了一部分的這個角色。也就是說,IE、Chrome、Firefox中內建有一些CA,經過這些CA頒發,驗證過的證書都是可信的,否則就會提示你不安全。
要開店的老闆去申請營業執照的時候是需要提交他的身份證的,然後搬出來的營業執照有他的照片和名字。身份證相當於私鑰,營業執照就是證書,Ceritificate,.cer檔案。
然後私鑰和公鑰他們在資料加密層面,資料的流向是這樣的。
訊息-->[公鑰]-->加密後的資訊-->[私鑰]-->訊息
公鑰是可以隨便扔給別人的,他把訊息加了密傳遞給我。可以這樣理解,我有一個箱子,一把鎖和一把鑰匙,我把箱子和開著的鎖給別人,他寫信放到箱子裡面,鎖上,然後傳遞迴我受傷的途中誰都是打不開箱子的,只有我可以用原來的鑰匙開啟,這就是SSL,公鑰、私鑰傳遞加密訊息的方式。這裡的金鑰就是.key檔案。
PEM,它是由RFC1421至1424定義的一種資料格式。其實裡面的.cert和.key檔案都是PEM格式的,只不過在有些系統(比如Windows)裡面會根據副檔名的不同而做不同的事情。所以當看到.pem檔案時,它裡面的內容可能是certificate也可能是key,也可能兩個都有,要看具體情況,可以通過OpenSSL檢視。而在與APNs的通訊中,我們需要傳遞的是同時包含.cert和.key的檔案。蘋果通過這個來確定你是否是開發者本人。
如何得到.pem檔案
1. 如果在Mac上鑰匙串訪問中能找到Apple Development iOS Push Server證書的話,可以在證書上面右鍵匯出生成apns_dev_cert.p12。如果沒有的話,可以在蘋果開發者中心生成aps_development.cer檔案,然後下載,雙擊匯入鑰匙串。
2. 然後在鑰匙串訪問中找到Apple DevelopmentiOS Push Server金鑰,右鍵生成apns_dev_key.p12。
3. 開啟終端,進入p12證書所在的目錄,輸入openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12 生成apns-dev-cert.pem
4. 輸入openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12 生成apns-dev-key.pem,要輸入密碼,記住這個密碼
5. 把兩個.pem檔案合併 cat apns-dev-cert.pem apns-dev-key.pem > apns-dev.pem
這樣就得到了開發環境的pem檔案了,如果是生產環境,那麼需要選擇生環境的證書並做相同的操作,得到apns-dis-cert.pem即可。
可以通過 openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert apns-dev-cert.pem -key apns-dev-key.pem 測試證書是否正常。執行完畢後,直接輸入任意字元,回車,出現closed,表示可用。否則列印錯誤資訊。
如果出現上面的錯誤資訊,那麼可能是OpenSSL的cert.pem檔案問題,可以在終端中輸入命令php -r "print_r(openssl_get_cert_locations());"來檢視default_cert_file的路徑,並且替換掉,可用版本的cert.pem在這裡(連結:https://pan.baidu.com/s/1IlOqpWK-ugenEEjaWqNf9A 密碼:7qmc),替換前列印下原檔案的許可權,替換後修改為同樣的許可權即可。
編寫PHP
.pem有了,那麼我們需要做的就是編寫PHP了。直接送上原始碼,需要注意的是修改pass變數為剛才生成.pem的密碼,替換deviceToken為測試裝置的token。如果是在開發環境,那麼選擇ssl://gateway.sandbox.push.apple.com:2195,如果是生產環境,那麼選擇ssl://gateway.push.apple.com:2195。程式碼如下:
<?php
$deviceToken= 'f020999681cc383f6a5e3efb8e3321912808ade966d20a3ec9696fbd5fa24e4c'; //沒有空格
$type = 1;//1 配圖2 逗圖工坊 3 文字表情
$body = 0;
if ($type == 1)
{
$body = array("aps" =>array("alert" => '配圖推送測試',"badge" => 1,"sound"=>'default'),
"flag" => "2",
"imageListName" => "我快瘋了!",
"imageListId" => 2589); //推送方式,包含內容和聲音
}
elseif ($type == 2)
{
$body = array("aps" => array("alert" => '逗圖工坊推送測試',"badge" => 1,"sound"=>'default'),
"flag" => "5",
"templateName" => "二次元",
"templateId" => 477); //推送方式,包含內容和聲音
}
elseif ($type == 3) {
$body = array("aps" =>array("alert" => '文字表情推送測試',"badge" => 1,"sound"=>'default'),
"flag" => "6"); //推送方式,包含內容和聲音
}
elseif ($type == 4)
{
$body = array("aps" =>array("alert" => '表情推送測試',"badge" => 1,"sound"=>'default'),
"flag" => "3",
"secondClassifyName" => "鬥圖應答",
"secondClassifyId" => 1033); //推送方式,包含內容和聲音
}
$ctx = stream_context_create();
//如果在Windows的伺服器上,尋找pem路徑會有問題,路徑修改成這樣的方法:
//$pem = dirname(__FILE__) . '/' . 'apns-dev.pem';
//linux 的伺服器直接寫pem的路徑即可
stream_context_set_option($ctx,"ssl","local_cert","apns-dev.pem");
$pass = "123456";
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
//此處有兩個伺服器需要選擇,如果是開發測試用,選擇第二名sandbox的伺服器並使用Dev的pem證書,如果是正是釋出,使用Product的pem並選用正式的伺服器
$fp = stream_socket_client("ssl://gateway.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
// $fp = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
echo"Failed to connect $err $errstrn";
return;
}
print"Connection OK\n";
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack("H*", str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
echo"sending message :". $payload ."\n";
fwrite($fp, $msg);
fclose($fp);
?>
執行測試
最後把push.php和apns-dev.pem放到同一目錄下,進入命令列輸入php push.php即可。
參考文獻:
1.https://www.zhihu.com/question/29620953
2.http://blog.sina.com.cn/s/blog_6f9a9718010128hi.html
3.https://www.cnblogs.com/cocoajin/p/3470704.html