一.關於推送通知
推送通知,也被叫做遠程通知,是在iOS 3.0以後被引入的功能。是當程序沒有啟動或不在前台運行時,告訴用戶有新消息的一種途徑,是從外部服務器發送到應用程序上的。一般說來,當要顯示消息或下載數據的時候,通知是由遠程服務器(程序的提供者)發送,然後通過蘋果的推送通知服務(Apple Push Notification Service,簡稱apns)推送到設備的程序上。
推送的新消息可能是一條信息、一項即將到期的日程或是一份遠程服務器上的新數據。在系統上展現的時候,可以顯示警告信息或在程序icon上顯示數字,同時,也可以播放警告音。一旦用戶注意到程序有新的信息、時間或是數據,他們可以運行程序並訪問新的內容。也可以選擇忽略通知,這時程序將不會被激活。
iPhone, iPad和iPod touch上同一時刻只有一個app在前台運行。大多數程序在後台運行的時候,可以對某些用戶感興趣的內容做出回應(定時、或數據等)。推送通知能讓程序在這些事件發生的時候通知用戶。
作為提供者為程序開發和部署推送通知,必須通過iOS Developer Program Portal獲得SSL證書。每個證書限用於一個程序,使用程序的bundle ID作為標識。證書有兩種用途的:一種是針對sandbox(用於開發和測試),另外一種針對發布產品。這兩種運行環境擁有為各自指定的IP地址並且需要不同的證書。還必須為兩種不同的環境獲取各自的provisioning profiles。
APNS提供了兩項基本的服務:消息推送和反饋服務。
消息推送:使用流式TCP套接字將推送通知作為二進制數據發送給APNs。消息推送有分別針對開發和測試用的sandbox、發布產品的兩個接口,每個都有各自的地址和端口。不管用哪個接口,都需要通過TLS或SSL,使用SSL證書來建立一個安全的信道。提供者編制通知信息,然後通過這個信道將其發送給APNs。 注:sandbox: gateway.sandbox.push.apple.com:219 產品接口:gateway.push.apple.com:2195
反饋服務:可以得到針對某個程序的發送失敗記錄。提供者應該使用反饋服務周期性檢查哪些設備一直收不到通知,不需要重復發送通知到這些設備,降低推送服務器的負擔。 注:sandbox:feedback.push.apple.com:2196 產品接口:feedback.sandbox.push.apple.com:2196
二.Apple Push Notification的工作機制
自己的客戶端發送消息到自己應用軟件的apns,apns發送消息到你的手機。這個是根據令牌識別的。手機在發送消息到你的應用軟件。
首先是應用程序注冊消息推送。
IOS跟APNS Server要deviceToken。應用程序接受deviceToken。
應用程序將deviceToken發送給PUSH服務端程序(Provider)。
服務端程序向APNS服務發送消息。
APNS服務將消息發送給iPhone應用程序。
1. Provider是指某個iPhone軟件的Push服務器,是我們將要開發的服務器。
2. APNS 是Apple Push Notification Service(Apple Push服務器)的縮寫,是蘋果的服務器。
上圖可以分為三個階段:
第一階段:推送服務器(provider)把要發送的消息、目的iPhone的標識打包,發給APNS;
第二階段:APNS在自身的已注冊Push服務的iPhone列表中,查找有相應標識的iPhone,並把消息發到iPhone;
第三階段:iPhone把發來的消息傳遞給相應的應用程序,並且按照設定彈出Push通知。
三 推送之前的配置
在任何iOS應用開發裡面,開發人員總是在團隊裡面扮演某一角色:團隊經理,團隊管理員,和團隊成員。iPhone開發證書和配置文件由不同角色完成。通常團隊經理是團隊裡面唯一可以創建Development SSL證書(沙箱環境)和Distribution SSL證書(產品環境)的人。而團隊經理和團隊管理員都可以創建Development和Distribution的配置文件。團隊成員只能下載並安裝證書和配置文件(!這裡要區別好證書和配置文件的概念:證書,即Certificates,它主要是使用Access Key Chain來生成的一個簽名密鑰。而配置文件,即Provisioning profiles,它主要是根據不同的App來生成的針對性的代碼簽名文件。二者區別是一般一個開發者賬戶只能有一個Certificates(而Development一個,Distribtuion一個),但是可以有多個Provisioning profiles文件,且這些Provisioning profiles通常創建的時候都使用同一個Certrificates)。以下部分詳細介紹該過程。
注意:iOS Provisioning Portal給所有iOS開發者。
創建SSL證書和密鑰
在iOS Dev Center的iOS Provisioning Portal裡面,團隊經理選中相應APNs的AppID。他需要完成以下步驟來創建SSL證書:
1.
點擊窗口左邊側邊欄的App IDs。
將會跳轉到一個顯示當前合法應用IDs的頁面。每個應用的ID前面包含了它的Bundle ID,而在Bundle ID之前包含了蘋果生成的十位字符串。團隊管理員必須輸入Bundle ID。對於每個證書,它必須匹配某一特定的Bundle ID,你不能使用“wildcart”的應用ID。即要使用推送通知的應用的Bundle ID不能使用“*”來創建。
2.
找到需要創建SSL證書的應用ID(和Development Provisioning Profile相關),並單擊Configure按鈕。
你必須確保該App ID下面的蘋果推送通知服務欄下面顯示“可用”狀態才能為該應用配置APNs證書。
3.
在App ID配置頁面,檢查已經勾選了“Enable for Apple Push Notification service”復選框,並點擊下面的“Configure”按鈕。
單擊該按鈕將會啟動APNs助理,它會通過一系列步驟來引導完成操作。
4.
第一步要求你啟動Keychain Access應用並生成一個Certificate Signing Request(CSR)文件。
下面的介紹來自證書助理。當你完成生成CSR後,點擊Keychain Access上面Continue來返回到APNs Assistant。
當你創建一個CSR時,Keychain Access生成一個私有和公有的加密密鑰對。而私有的密鑰會默認被放入你的Login keychain裡面。而公有的密鑰包含CSR會發送到配置服務器。當配置服務器把證書發回給你的時候,證書裡面的其中一個條目將是公有密鑰。
5.
在Submit Certificate Signing Request面板上,單擊Choose File。導航到你之前創建的CSR文件的地方並選中它。
6.
單擊Generate按鈕。
在顯示Generate Your Certificate面板的同時,Assistant會配置並生成你的客戶端SSL證書。如果生成成功,它將會顯示信息“Your APNs Certificate has been generated.”。單擊Continue來進入下一步操作。
7.
在下一個面板,單擊Download Now按鈕來下載證書文件到本地文件夾。找到文件下載的地方並雙擊證書文件(該證書文件包含一個.cer的擴展名)來把它安裝到你的keychain裡面。當完成後,單擊APNs Assistant上面的Done按鈕。
雙擊加載啟動Keychain Access。確保你已經把剛才的證書安裝到了你用於開發的電腦的login keychain裡面。在Keychain Access裡面,確保你的證書使用的ID匹配你應用的Bundle ID。APNs的SSL證書應該安裝到你的通知服務上面。
當你返回到iOS Dev Center Portal的Configure App ID頁面完成這些步驟後,你的證書應該會變成綠色,並且顯示“Enabled”。
為了給產品環境創建一個證書,重復上述步驟,但記住選中產品證書的應用ID。
團隊經理或團隊管理器接下來必須創建在服務器用於遠程通知開發的配置證書(Development 或 Distribution)。配置文件就是一個集合,它囊括了和應用相關的開發者和開發團隊驗證過的設備並使用這些設備來測試應用程序。配置文件包含了證書、設備標示符、應用的Bundle ID、和所有權利,包括<aps-environment>。所有團隊程序需要在運行並測試他們應用代碼的設備上面安裝該配置文件。
注意:關於創建配置文件的詳細解析參考編程用戶指南。
為了下載並安裝配置文件,團隊程序必須完成以下步驟:
1.
進入iOS Dev Center的Provisioning portal頁面。
2.
創建一個新的配置文件,並包含你注冊用於APNs的App ID。
3.
在你下載這個新的配置文件之前修改任何已存在的配置文件。
你必須修改配置文件的一些細微部分(比如切換選項)來生成一個新的配置文件。如果配置文件並沒有“受損(dirtied)”,你不應該給以該原始配置文件任何推送的權利。
4.
找到文件的下載目錄,把該配置文件(通常是一個.mobileprovision擴展文件)拖拉到Xcode或iTunes應用程序的圖標上面。
可選的,你也可以把配置文件復制到~/Library/MobileDevice/Provisioning Profiles目錄。如果當前目錄不存在則生成一個新目錄。
5.
驗證該配置文件的權利是否正確。為此,使用文本編輯器打開.mobileprovision文件。該文件的內容是一個XML的文本。查看在aps-environment鍵的位置的字典值。對於一個開發模式的配置文件,該值應該是development;而對應發布模式的配置文件,該字符值應該是production。
6.
在Xcode的Organizer窗口,查看Provisioning Profiles部分,並確認證書已經安裝到你的設備上面。
當你編譯該工程的時候,二進制文件現在使用證書的私有密鑰簽名。
4.2.3
安裝SSL證書和密鑰到你的服務器上面
你必須安裝SSL發布證書和你之前獲取的私有加密密鑰到需要運行提供者代碼的服務器上,從該服務器連接到APNs的沙箱或產品環境。為此,需要完成以下步驟:
1.
打開Keychain Access實體並單擊左邊面板的My Certificates類別。
2.
找到你將要安裝的證書,並打開相應內容。
你將會看到證書和私有密鑰。
3.
選中證書和密鑰,選中File > Export Items,並把它們導出個人信息交換文件(.p12)。
4.
如果服務器采用Buby或Perl語言來實現的話,那麼它們更容易處理個人信息交換格式的文件。為了把證書轉換為該格式,需要完成以下步驟:
a)
在Keychain Access裡面,選中相應證書並選擇File > Export Items。選擇個人信息交換格式選項(.p12)。選擇一個保存地址,並單擊Save按鈕。
b)
加載終端應用,並輸入如下的命令:
openssl pkcs12 -in CertificateName.p12 -out CertificateName.pem -nodes
5.
拷貝.pem證書文件到新的電腦並安裝它到合適的地方。
這次准備工作已經完成了。
下面是客戶端的步驟了
一個應用程序必須在設備(iOS設備或Mac電腦)上面注冊了蘋果推送通知服務才能接收來自程序提供者的遠程通知。
注冊過程包含以下三個步驟:
一個應用程序必須在設備(iOS設備或Mac電腦)上面注冊了蘋果推送通知服務才能接收來自程序提供者的遠程通知。
注冊過程包含以下三個步驟:
1.
程序調用registerForRemoteNotificationTypes:方法。
2.
委托實現application:didRegisterForRemoteNotificationsWithDeviceToken:方法來接收設備令牌。
3.
把設備令牌作為非對象(二進制值)傳遞給程序提供者。
1.
程序調用registerForRemoteNotificationTypes:方法。
2.
委托實現application:didRegisterForRemoteNotificationsWithDeviceToken:方法來接收設備令牌。
3.
把設備令牌作為非對象(二進制值)傳遞給程序提供者。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[ViewController alloc] init] autorelease];
self.window.rootViewController = self.viewController;
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"background.png"]]];
[self.window makeKeyAndVisible];
/** 注冊推送通知功能, */
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
//判斷程序是不是由推送服務完成的
if(launchOptions) {
NSDictionary* pushNotificationKey = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(pushNotificationKey) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"推送通知"
message:@"這是通過推送窗口啟動的程序,你可以在這裡處理推送內容"
delegate:nil
cancelButtonTitle:@"知道了"
otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
returnYES;
}
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString* token = [NSString stringWithFormat:@"%@",deviceToken];
NSLog(@"apns -> 生成的devToken:%@", token);
//把deviceToken發送到我們的推送服務器
DeviceSender* sender = [[[DeviceSender alloc]initWithDelegate:self ]autorelease];
[sender sendDeviceToPushServer:token ];
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"apns -> 注冊推送功能時發生錯誤, 錯誤信息:\n %@", err);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(@"\napns -> didReceiveRemoteNotification,Receive Data:\n%@", userInfo);
//把icon上的標記數字設置為0,
application.applicationIconBadgeNumber = 0;
if([[userInfo objectForKey:@"aps"] objectForKey:@"alert"]!=NULL) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"**推送消息**"
message:[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]
delegate:self
cancelButtonTitle:@"關閉"
otherButtonTitles:@"處理推送內容",nil];
alert.tag = alert_tag_push;
[alert show];
}
}