本文由泰然教程組新人1V1計划出品,http://article.ityran.com/archives/194
翻譯人員:曉丫 &小樣,Sharyu,大菠蘿,outman;校對人員:子龍山人,Iven,sile,TXX_糖炒小蝦
學習怎樣為你的iphone程序添加推送通知
這是由iOS教程組成員Matthijs Hollemans撰寫的一篇有關推送通知的教程,Matthijs Hollemans是一位有着豐富經驗的iOS開發者和設計者。
在iOS系統里,應用程序在后台運行的時候有很多事情都做不了(譯者注:由於沙盒限制)。應用程序只允許在這個“沙盒”內做一些操作,這樣可以減緩電池的消耗,延長電池的使用時間。
但是,假如用戶現在沒有在用你的程序,而你的程序恰好發生了一些有趣的事情,而你也想讓用戶知道,這時你該怎么做?
例如,用戶收到了一條tweet的新消息,他們喜歡的隊伍贏得了比賽,或者晚飯已經准備好了。因為應用現在沒有在運行,程序就接收不到這些新消息。
幸運的是,蘋果公司為這種情況提供了一個解決辦法。你可以寫一個服務器端的組件來解決這樣的問題,而不是讓應用不斷的在后台運作,檢查事件的發生。當一些有趣的事件發生的時候,服務器端的組件可以發送一個推送通知!
下面是推送通知可以做的三件事:
·顯示一條短信息
·播放一個簡短的提示聲音
·在程序的圖標上設置推送消息數量
你可以根據需要,隨意組合以上三項功能;例如,你可以播放簡短的提示聲音和設置應用圖標上推送消息的未讀數量,但不顯示消息。
在這兩部分的系列教程里,你將通過創建一個簡單使用APNS(蘋果的推送通知)的app,來實現推送通知。
首先,在第一部分教程里面,你將學習怎么設置應用程序,來接受推送通知和一條測試消息。
這篇教程是為中高級iOS開發者寫的。如果你只是一個iOS開發的初學者,應該先學習這個網站的其他教程。所以,對於初學者,強力推薦先學習下面的兩篇教程:
閑話少說,讓我們把完成它!
簡要概述
想讓應用程序的推送功能正常工作,你需要相當多的努力,這個過程非常繁瑣。以下是這個過程的概述圖:
1.應用程序需要激活推送通知功能。在使用之前,用戶需要確認他是否願意接受這些推送功能。
2.應用程序接到一個“device token”。你可以認為“device token”是推送通知發送信息的目的地址。
3.應用程序會將“device token”發送給你的服務器
4.當你的應用程序發生了有趣的事情,你的服務器向 “蘋果推送通知的服務器(Apple Push Notification Service,縮寫,APSN)”發送通知。
5.APSN再向用戶的設備(例如,iPhone,iPad等)發送通知。
當用戶的設備接受到推送通知,將會顯示提醒框,播放提示聲音,更新圖標的未讀信息數目。用戶可以通過點擊提醒框來加載應用程序,蘋果公司給推送通知加入了可選內容,你可以根據需求來操作推送通知。
當iOS4實現了本地通知和多任務,蘋果的推送通知是否還有使用價值?當然有!
本地通知被定時事件調度限制,並且只有VOIP、導航、后台音頻播放這些應用在后台沒有被限制。如果當應用程序處於關閉狀態時,你想將外部事件的發生通知給你的應用程序用戶,仍然需要推送通知。
在這篇教程中,我將解釋推送通知系統實現的細節以及如何在應用中建立推送。這里有很多要解釋的,這需要(您)花點兒時間去明白。
你需要為推送做些什么
在你的應用中添加推送通知,你需要:
一台iPhone或者iPad 推送通知不能在模擬器上實現,所以你需要在設備上測試。
一個iOS開發者證書 你需要一個新的AppID和每個應用程序使用的推送證書,推送服務器需要的“SSL ”證書,你可在iOS開發網站上做這些。
如果你想跟隨這篇教程的例子學習,你將需要創建你自己的推送證書和SSL證書,你不可以使用我的這些證書。因為獲得正確的證書是很重要的,我將詳細解釋如何得到一個推送證書。
一個聯網的服務器 推送通知通常是由服務器來發送的。對於開發,你可以使用一個Mac來代替服務器(我們將在教程中這樣做),但是發布的產品,你至少需要類似VPS( Virtual Private Server )的服務器。
一個廉價的共享虛擬主機賬戶不能滿足發布產品的需要。你需要在服務器的后台啟動一個進程,安裝一個SSL證書,並且能夠在某個端口中能夠外聯TLS。
大部分的共享虛擬主機並不讓你實現這些功能,即使如果申請的這些需求通過了。無論怎么樣,我真的建議你使用一個VPS主機,像Linode。
解析一個推送通知
你的服務器負責創建一個推送通知消息,所以了解一個推送通知消息的構成是有必要的。
一個推送通知是一個短信息,由“device token”,“payload–負載內容”,和其他的一些字節組成。“payload–負載內容”是我們感興趣的部分,因為它包含着我們實際想發送出去的數據。
你的服務器必須提供“payload–負載內容”,它是以JSON的字典的數據格式來組織數據的。下面是一個很簡單的推送消息payload:
{ "aps": { "alert": "Hello, world!", "sound": "default" } }
對於不了解JSON的人,一個block的划分由一對花括號“{}”包裹,其中包含一個由“鍵/值 (key/value)”對組成的“字典”,(就像NSDictionary)。
“payload–負載內容”就是一個“字典”,包含了至少一個“aps”項,“aps”本身也是一個“字典”。在我們的例子中,“aps”包含“alert”和“sound”字段。當這個推送通知被接收后,它將顯示一個包含“Hello, world!”內容的提醒框,並且播放標准的提示音。
你可以向“aps”添加另外的選項,來配置通知,例如
{ "aps": { "alert": { "action-loc-key": "Open", "body": "Hello, world!" }, "badge": 2 } }
現在“alert”是一個字典。”action-loc-key” 對應的value替代了“View”按鈕上的文本內容,”badge”字段包含的數字將被顯示在應用圖標上,這個通知不會播放提示音。
有很多途徑去設置JSON的“payload–負載”內容,你可以改變播放聲音,你可以提供本地化的文本,並且添加自己的字段。更多信息,請詳見官方Local and Push Notification Programming Guide。
推送通知的目標就是精短;“payload–負載內容”的大小不能超過256個字節。這樣留給你的空間和一條短消息或者一個tweet消息的大小一樣。一個小型的推送服務不會在換行符和空格上浪費空間,這樣一條推送就像下面所顯示的:
{“aps”:{“alert”:”Hello, world!”,”sound”:”default”}}
上面這條消息可讀性比較低,但是它節省了足夠的字節,所以犧牲可讀性是值得的。如果一個推送通知的payload超過了256個字節,那么這個推送就不會被“APNS”接受。
關於推送通知常見的錯誤
推送通知是不可靠的
推送通知是不可靠的!即使APNS(Apple Push Notification Service蘋果推送通知服務)服務器接受了推送通知,仍然無法保證該通知最終會被送達。
就你的服務器而言,推送通知會被發出並且遺忘掉;當你將通知發送到APNS后,沒有辦法查出它所處的狀態。通知送達的時間也從幾秒到半小時不等。
同樣,用戶的iPhone不是所有時間都可以收到推送通知。比如,因為指定的端口被封,手機處於一個不允許和APNS連接的WIFI網絡。或者手機已經關機了。
APNS將會在手機連接到可用網絡后下發從該機器收到的最后一條通知,但是只會嘗試有限的時間。一旦發送超時,此條通知就會永遠丟失!
After looking at the APNS Server Bill
推送通知會使開銷很大!如果你掌控了需要推送的內容,在你的應用中加入推送功能相當容易和廉價,但是當你有好多用戶和數據需要輪詢的時候,這個功能就會使得服務器開銷龐大(譯者注:不一定指價格,包括是資源消耗)。
比如,你打算在自己的RSS訂閱更新的時候通知用戶,這樣做沒問題。因為你控制着RSS訂閱,並且知道它何時發生變化——當你更新網站內容時——於是你的服務器可以在合適的時候發出通知。
但是如果你的應用是一款RSS閱讀器,允許用戶添加自定義的鏈接呢?這種情況下,你需要構建一些機制去檢測那些訂閱的更新。
實際上,這意味着你的服務器為了檢查那些訂閱的變化,需要不停的輪詢它們。如果你擁有很多用戶,你可能不得不安裝一堆新服務器去處理這些工作和滿足帶寬需求。對於這類應用,推送會變得異常昂貴,可能不值得去做。
好了,理論上足夠了。下面到時間來學習如何使用推送了。在我們投入到美好的事物——編程!之前——有一些無聊的搭建環境的工作需要在iOS Provisioning Portal上完成,所以讓我們盡快完成它。
Provisioning Profiles和證書,天哪!
APNS需要一個證書
在你的應用中使用推送通知,需要用一個配置過推送功能的provisioning profile來簽名。此外,你服務器所有與APNS的通訊都需要進行SSL證書簽名。
Provisioning profile和SSL證書緊密聯系在一起,並且只對一個App ID有效。這個保護措施可以保證只有你的服務器可以發推送通知到你的應用,其它服務器不可以。
正如你所知道的,應用程序在開發和發布階段使用不同的provisioning profiles。同樣,推送服務器的證書也有兩種:
· Development. 如果你的應用程序運行在debug模式,並且使用的是Development provisioning profile (Code Signing Identity 是 “iPhone Developer”)簽名的,你的服務器必須使用Development證書。
· Production. 使用Ad Hoc方式發布的,或發布在App Store(Code Signing Identify 是 “iPhone Distribution”)上的應用程序,必須和使用Production證書簽名的服務器通訊。如果這里面有不匹配,推送通知將無法送達你的應用。
在這篇教程里,我們不需要為分發profiles和證書煩惱,只需要使用Development版本的即可。
生成證書簽名請求(Certificate Signing Request, CSR)
還記得你在注冊成為iOS開發者之后,如何去iOS Provisioning Portal生成一個開發證書嗎?如果記得,下面的步驟應該會比較熟悉。不過,我仍然建議你准確地按照步驟來做。因為大多數在實現推送通知過程中遇到的問題,都是由於證書問題引起的。
數字證書基於公鑰-私鑰加密方法。你不需要知道任何關於證書的加密方法,但是你要知道證書一直會與一個私鑰搭配使用。
證書是密鑰對的非秘密的部分。將它發送給其它人是安全的,比如通過SSL通訊的過程中就會包含證書。然而,對於私鑰,當然是私有的。它是秘密的。你的私鑰只對你有用,對其他人沒用。要重視的是:如果你沒有私鑰的話,就無法使用證書。
每當申請一個數字證書的時候,你需要提供一個證書簽名請求,簡稱CSR。當你創建了CSR后,會生成一個新的私鑰保存到keychain應用程序中。然后你將CSR發送到一個證書頒發機構(目前情況下就是iOS Developer Portal),它會根據CSR中的信息生成SSL證書。
打開Mac中的Keychain Access程序(在Applications/Utilities下),選擇菜單中的Request a Certificate from a Certificate Authority…
如果菜單中沒有“Request a Certificate from a Certificate Authority with key”選項,就先去下載安裝WWDR Intermediate Certificate。並且確認Keychain Access窗口里沒有私鑰被選中。
現在,你應該會看到下面的窗口:
在里面輸入你的郵件地址,聽有人推薦說最好使用和注冊IOS開發者證號同樣的郵件地址,但看起來任何郵件地址都可以。在Common Name中輸入“PushChat”。你可以輸入任何字符串,但最好是有意義的。這會使你以后容易查找這個私鑰。
選擇Saved to disk選項,點擊Continue。將文件保存為“PushChat.certSigningRequest”。
如果你切換到Keychain Access軟件的Keys標簽,你將會看到一個新的私鑰出現在你的鑰匙串里。右鍵點擊它,選擇Export。
將私鑰保存為“PushChatKey.p12”,輸入一個密碼短語。
為了教程的方便,我用了密碼短語“pushchat”來保護這個p12文件,但是你應該選擇一些更不容易被猜出的。記住,私鑰是要保密的。另外,一定要選擇你能記住的密碼短語,否則以后就無法使用這個密鑰了。
一個非常簡單的程序
目前還沒有激動人心的地方,但是前面的那些准備工作是必須的。我將會詳細介紹如何生成證書,因為它不是你每天都要做的,但是沒有證書推送就不能工作。
因為可以連接到沙盒服務器,所以證明我們的證書是有效的。讓我們來測試一下,是不是真的能推送一些消息!
點擊Xcode然后選擇File,創建一個新的Project。選擇View-based Application模板,然后下一步
在文本框填入下列值:
· Product Name項目名:PushChat
· Company Identifier公司ID: com.hollance
· Device Family設備: iPhone
Bundle ID由項目名稱和公司ID組成。我的項目ID叫“com.hollance.PushChat”
你應該讓你的Product Name和Company Identifier 與你早先用App ID在Provisioning Portal 注冊的相一致(com.yourname.PushChat).
完成后打開PushChatAppDelegate.m,將didFinishLaunchingWithOptions 方法改成下面的樣子:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; //讓設備知道我們想要收到推送通知 [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; return YES; }
調用registerForRemoteNotificationTypes 告訴OS 這個App想要接收推送消息。
編譯運行。你需要在真機上運行這個程序,因為模擬器不支持推送通知。
Xcode應該會自動選擇新的provisioning profile。如果你得到一個簽名錯誤,那么請確認在Code Signing build settings中選擇了正確的profile。
當應用程序啟動了推送通知,它會顯示一個消息通知用戶,它想推送通知。
這個app推送請求只出現一次。如果用戶選“確定”,那么我們的推送通知就全設置好了。
然而,如果他們選擇“不允許”,那么我們的應用程序將不會接收到推送通知。用戶可以在手機設置里改變他們的決定。
你可以在setting -> Notifications里面找到你的應用程序,用戶可以在這里啟用或禁用
應用程序的通知,包括標記、聲音和警報。
你的app可以查看哪些推送類型被啟用,用以下代碼來查看
UIRemoteNotificationType enabledTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
為了能收到推送消息,我們還要在app中添加一些內容。將下列代碼添加到PushChatAppDelegate.m中:
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { NSLog(@”My token is: %@”, deviceToken); } - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { NSLog(@"Failed to get token, error: %@", error); }
當你的應用程序注冊遠程通知時,它將會嘗試獲得一個“設備標記(device token)”。
這是一個32字節數字,標識你的設備的唯一性。 可以把device token理解為推送消息的接收地址。
再次運行程序,你應該能在Xcode的控制台窗口看到下面這個:
My token is:
<740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad>
這個標識是一個封裝的二進制數據結構,裝入一個NSData對象里。蘋果不希望你看見它的內部信息,就我們而言知道它是32字節長就夠了。
正如你上面所看到的,標識也可用64位十六進制字符表示,我們將用這種格式使用設備標識,當然還要去掉分隔符和空格。
如果你在模擬器上運行這個程序,由於你的模擬器不支持推送通知,didFailToRegisterForRemoteNotificationsWithError方法將被調用。
這個應用就是這樣。還有一件事要做,之后我們馬上就可以看到一些推送通知,立即行動!
我之前已經提到了幾次,你需要創建一個服務器,它將推送通知給你的app。
第一次測試程序,我們不會去建立一個服務器。相反,我會給你們一個非常簡單的PHP腳本,
,來建立一個連接到APNS並發送一個推送通知到您所指定的設備。你可以直接在mac電腦上運行這個腳本
下載這些SimplePush代碼並解壓縮,你需要在simplepush.php做些改變。
//把你的設備標識寫在這里(沒有空格): $deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78'; //密碼放在這里 $passphrase = 'pushchat'; // 把你的推送消息放在這里: $message = 'My first push notification!';
你需要從app中拷貝device token到$deviceToken變量。確定刪掉了空格以及分隔符。它應該是64位的十六進制字符。把你私鑰密碼短語放到$passphrase變量,以及你想發送的信息放到$message中。
拷貝你的ck.pem 文件到SimplePush目錄,記住,這個ck.pem文件同時包含你的證書和私鑰。
然后打開一個終端並鍵入:
如果一切順利,腳本應該會顯示:
Connected to APNS
Message successfully delivered
注意, 當應用程序是開着的你不會看到任何東西。消息傳過來了,但是我們在app中沒有做任何處理消息的方法。
如果顯示一些錯誤信息,simplepush.php腳本退出,檢查你是否正確制作PEM文件,
現在,腳本究竟做了什么並不重要。在這個系列的第二部分,我們會建立一個真正的推送服務器,到那時候我們會就此做更多的說明。
此時,你已經成功建立了一個app來接收推送通知,並且通過自定義的PHP代碼發送了第一條推送通知。
稍后請看教程系列的Part2。