從上圖我們可以看到:
1沐寺、應(yīng)用程序注冊(cè)消息推送林艘。
2、iOS從APNS Server獲取device token混坞,應(yīng)用程序接收device token北启。
3、應(yīng)用程序?qū)evice token發(fā)送給PUSH服務(wù)端程序拔第。
4咕村、服務(wù)端程序向APNS服務(wù)發(fā)送消息。
5蚊俺、APNS服務(wù)將消息發(fā)送給iPhone應(yīng)用程序懈涛。
關(guān)于deviceToken:
deviceToken生成:
遠(yuǎn)程通知首先要向蘋(píng)果APNs服務(wù)器注冊(cè)并且生成一個(gè)唯一的deviceToken(每個(gè)設(shè)備的客戶端都獨(dú)一無(wú)二)。根據(jù)向APNs服務(wù)器發(fā)送的Token key(包含了設(shè)備的UUID和App的Bundle Identifier)泳猬。deviceToken將會(huì)以NSData對(duì)象發(fā)送到對(duì)應(yīng)請(qǐng)求的App上批钠。然后App把此deviceToken發(fā)送給我們自己的服務(wù)器,就是所謂的Provider得封。Provider收到deviceToken以后進(jìn)行儲(chǔ)存等相關(guān)處理埋心,以后Provider根據(jù)這個(gè)deviceToken來(lái)進(jìn)行消息推送。
deviceToken用處:
deviceToken是推送服務(wù)器(Provider)在向應(yīng)用推送消息時(shí)忙上,找到對(duì)應(yīng)的設(shè)備以及該設(shè)備上對(duì)應(yīng)的應(yīng)用拷呆,從而把此推送消息推送給此應(yīng)用。
deviceToken唯一性:
deviceToken根據(jù)設(shè)備唯一標(biāo)識(shí)和客戶端唯一標(biāo)識(shí)生成疫粥。確保了deviceToken唯一茬斧。唯一性并不是說(shuō)一臺(tái)設(shè)備上的一個(gè)應(yīng)用程序永遠(yuǎn)只有一個(gè)deviceToken,當(dāng)用戶升級(jí)系統(tǒng)的時(shí)候deviceToken是會(huì)變化的梗逮。
注冊(cè)遠(yuǎn)程通知(獲取deviceToken)
注冊(cè)遠(yuǎn)程通知的方法
一般都是在App啟動(dòng)完成的時(shí)候去注冊(cè)遠(yuǎn)程通知注冊(cè)方法調(diào)用一般都在didFinishLaunchingWithOptions:方法中
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// 在iOS8之前注冊(cè)遠(yuǎn)程通知的方法项秉,如果項(xiàng)目要支持iOS8以前的版本,必須要寫(xiě)此方法
UIRemoteNotificationTypetypes=UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert;
[[UIApplicationsharedApplication] registerForRemoteNotificationTypes:types];
// iOS8之后注冊(cè)遠(yuǎn)程通知的方法
UIUserNotificationTypetypes=UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings*mySettings =[UIUserNotificationSettingssettingsForTypes:types categories:nil];
[[UIApplicationsharedApplication] registerUserNotificationSettings:mySettings];
}
處理注冊(cè)遠(yuǎn)程通知的回調(diào)方法
// 注冊(cè)成功回調(diào)方法慷彤,其中deviceToken即為APNs返回的token
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[self sendProviderDeviceToken:deviceToken];// 將此deviceToken發(fā)送給Provider
}
// 注冊(cè)失敗回調(diào)方法娄蔼,處理失敗情況
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {}
處理接收到遠(yuǎn)程通知消息分兩種情況:前臺(tái)和后臺(tái)
application: didFinishLaunchingWithOptions:此方法在程序在第一次啟動(dòng)時(shí)調(diào)用怖喻,根據(jù)方法內(nèi)代碼判斷是否有推送消息。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// userInfo為收到遠(yuǎn)程通知的內(nèi)容
NSDictionary*userInfo=launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
// 有推送的消息岁诉,處理推送的消息
}
return YES罢防;
}
各種狀態(tài)下APP收到消息以及處理:
首先不管在前臺(tái)還是在后臺(tái),如果設(shè)置后臺(tái)模式為Remote Notifications
具體設(shè)置方式(或者在info.plist中配置了UIBackgroundModes):如圖
TARGETS-Capabilities-Background Modes-Remote Notifications
此時(shí)不論App處于Foreground狀態(tài)還是處于Background狀態(tài)唉侄,收到遠(yuǎn)程推送消息的時(shí)候都會(huì)立即調(diào)用此方法咒吐。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ }
這里引入一個(gè)概念:
**推送喚醒(remote notifications)
**iOS7以前,當(dāng)你收到推送消息時(shí)属划,你需要先打開(kāi)應(yīng)用恬叹,等待應(yīng)用從網(wǎng)絡(luò)上獲取推送的信息之后,才能將信息呈現(xiàn)出來(lái)同眯。而iOS7改變了這一過(guò)程绽昼。當(dāng)系統(tǒng)收到推送消息時(shí),不是首先提醒用戶须蜗,而是喚醒對(duì)應(yīng)的應(yīng)用硅确,讓?xiě)?yīng)用在后臺(tái)獲取對(duì)應(yīng)的信息。當(dāng)信息處理完成后明肮,再提醒用戶菱农。一個(gè)很小的改變,但是可以很大的提升用戶體驗(yàn)柿估。同樣循未,iOS系統(tǒng)也會(huì)限制這種推送消息的頻率,防止系統(tǒng)被頻繁喚醒影響續(xù)航秫舌。
此時(shí)需要更改推送的payload的妖,如果想要使用推送來(lái)喚醒應(yīng)用運(yùn)行代碼的話,需要在payload中加入content-available足陨,并設(shè)置為1嫂粟。
aps {
content-available: 1
alert: {...}
}
1.程序在前臺(tái)(Foreground)時(shí)收到推送:
如果設(shè)置remote notifications,那么先執(zhí)行相對(duì)應(yīng)的方法墨缘,在后臺(tái)收到推送也是如此星虹。
在前臺(tái)收到通知時(shí),會(huì)調(diào)用下面這個(gè)方法,可以在這個(gè)方法里面實(shí)現(xiàn)收到通知時(shí)刷新或跳轉(zhuǎn)界面的功能飒房;程序在前臺(tái)收到推送時(shí)通知欄不會(huì)彈出推送信息
-(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo{}
2.程序在后臺(tái)時(shí)收到推送:
如果設(shè)置remote notifications搁凸,那么先執(zhí)行相對(duì)應(yīng)的方法媚值,在前臺(tái)收到推送也是如此狠毯。
如果用戶點(diǎn)擊通知欄信息進(jìn)入程序會(huì)調(diào)用情況1中的方法,所以在情況1的方法里面需要根據(jù)程序在前臺(tái)還是后臺(tái)來(lái)執(zhí)行不同操application.applicationState
3.當(dāng)程序關(guān)閉時(shí)收到推送:程序關(guān)閉時(shí)收到推送時(shí)褥芒,用戶點(diǎn)擊通知欄信息進(jìn)入應(yīng)用的時(shí)會(huì)調(diào)用
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// 在此方法中一定要調(diào)用completionHandler這個(gè)回調(diào)嚼松,告訴系統(tǒng)是否處理成功
UIBackgroundFetchResultNewData, // 成功接收到數(shù)據(jù) UIBackgroundFetchResultNoData, // 沒(méi)有接收到數(shù)據(jù) UIBackgroundFetchResultFailed // 接受失敗
if (userInfo) {
completionHandler(UIBackgroundFetchResultNewData);
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
可操作通知類型收到推送消息時(shí)回調(diào)方法
// 此兩個(gè)回調(diào)方法對(duì)應(yīng)可操作通知類型嫡良,具體使用方法參考以上方法很容易理解,不在詳細(xì)敘述
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler {
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
}
未讀消息數(shù)量角標(biāo)設(shè)置
設(shè)置角標(biāo)
[UIApplication sharedApplication].applicationIconBadgeNumber=badgeNum;
這個(gè)方法可以設(shè)置應(yīng)用程序的角標(biāo)的數(shù)值献酗。但是當(dāng)程序關(guān)閉時(shí)寝受,收到推送后我們就不能改變角標(biāo)數(shù)值了。所以建議讓服務(wù)端推送過(guò)來(lái)的信息里加上'badge' = 88這個(gè)鍵值對(duì)來(lái)確定角標(biāo)的顯示數(shù)值罕偎。這樣程序在后臺(tái)還是關(guān)閉很澄,只要顯示服務(wù)端傳給我們的角標(biāo)值就好了。
不過(guò)當(dāng)我們閱讀完一條消息的時(shí)需要告訴服務(wù)器颜及,并且將[UIApplication sharedApplication].applicationIconBadgeNumber減一甩苛。
當(dāng)客戶端殺死情況走本地推送(當(dāng)客戶端開(kāi)啟走—(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions)。
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
// 當(dāng)被殺死狀態(tài)收到本地通知時(shí)執(zhí)行的跳轉(zhuǎn)代碼
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NSDictionary *infoDict = [mnResource parseJSONStringToNSDictionary:notification.userInfo[@"info"]];
[self.delegate diaplayAlertWhenReceivePushInfo:infoDict];
}
到現(xiàn)在為止做iOS 推送也差不多算入行了吧(都是用的個(gè)推)
昨天調(diào)試個(gè)推送也讓我蛋疼俏站,調(diào)試了一晚上讯蒲,其他系統(tǒng)的沒(méi)什么問(wèn)題,就有個(gè)手機(jī)是iOS 10.0.2的系統(tǒng)肄扎,不知道為什么走不通apns墨林。
遇到這些問(wèn)題不要慌,先定為到問(wèn)題犯祠。下面列舉自己踩過(guò)的個(gè)推方面的坑:
1.打包的證書(shū)與個(gè)推后臺(tái)的證書(shū)環(huán)境不一致旭等,導(dǎo)致推送失敗。
2.服務(wù)端忘記設(shè)置了個(gè)推安卓和ios的key衡载,沒(méi)有區(qū)分辆雾,所以會(huì)向客戶端下發(fā)兩條推送,那是我解析的時(shí)候是蒙蔽的月劈,json解析出來(lái)怎么會(huì)有兩種key完全不同的字典度迂。這時(shí)候alert很大幾率顯示null且不能跳轉(zhuǎn),因?yàn)槟憬馕霾怀鰜?lái)猜揪。
3.上面那種講到的情況惭墓,自己分析應(yīng)該是
這里在說(shuō)明一點(diǎn)在使用個(gè)推的時(shí)候上傳個(gè)推平臺(tái)的.p12要和打包的證書(shū)環(huán)境一致哦(生產(chǎn)環(huán)境和調(diào)試環(huán)境)
參考:http://www.reibang.com/p/4b947569a548