
從上圖我們可以看到:
1、應(yīng)用程序注冊消息推送。
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)程通知首先要向蘋果APNs服務(wù)器注冊并且生成一個唯一的deviceToken(每個設(shè)備的客戶端都獨(dú)一無二)。根據(jù)向APNs服務(wù)器發(fā)送的Token key(包含了設(shè)備的UUID和App的Bundle Identifier)。deviceToken將會以NSData對象發(fā)送到對應(yīng)請求的App上。然后App把此deviceToken發(fā)送給我們自己的服務(wù)器,就是所謂的Provider。Provider收到deviceToken以后進(jìn)行儲存等相關(guān)處理,以后Provider根據(jù)這個deviceToken來進(jìn)行消息推送。
deviceToken用處:
deviceToken是推送服務(wù)器(Provider)在向應(yīng)用推送消息時,找到對應(yīng)的設(shè)備以及該設(shè)備上對應(yīng)的應(yīng)用,從而把此推送消息推送給此應(yīng)用。
deviceToken唯一性:
deviceToken根據(jù)設(shè)備唯一標(biāo)識和客戶端唯一標(biāo)識生成。確保了deviceToken唯一。唯一性并不是說一臺設(shè)備上的一個應(yīng)用程序永遠(yuǎn)只有一個deviceToken,當(dāng)用戶升級系統(tǒng)的時候deviceToken是會變化的。
注冊遠(yuǎn)程通知(獲取deviceToken)
注冊遠(yuǎn)程通知的方法
一般都是在App啟動完成的時候去注冊遠(yuǎn)程通知注冊方法調(diào)用一般都在didFinishLaunchingWithOptions:方法中
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// 在iOS8之前注冊遠(yuǎn)程通知的方法,如果項(xiàng)目要支持iOS8以前的版本,必須要寫此方法
UIRemoteNotificationTypetypes=UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert;
[[UIApplicationsharedApplication] registerForRemoteNotificationTypes:types];
// iOS8之后注冊遠(yuǎn)程通知的方法
UIUserNotificationTypetypes=UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings*mySettings =[UIUserNotificationSettingssettingsForTypes:types categories:nil];
[[UIApplicationsharedApplication] registerUserNotificationSettings:mySettings];
}
處理注冊遠(yuǎn)程通知的回調(diào)方法
// 注冊成功回調(diào)方法,其中deviceToken即為APNs返回的token
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[self sendProviderDeviceToken:deviceToken];// 將此deviceToken發(fā)送給Provider
}
// 注冊失敗回調(diào)方法,處理失敗情況
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {}
處理接收到遠(yuǎn)程通知消息分兩種情況:前臺和后臺
application: didFinishLaunchingWithOptions:此方法在程序在第一次啟動時調(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收到消息以及處理:
首先不管在前臺還是在后臺,如果設(shè)置后臺模式為Remote Notifications
具體設(shè)置方式(或者在info.plist中配置了UIBackgroundModes):如圖
TARGETS-Capabilities-Background Modes-Remote Notifications

此時不論App處于Foreground狀態(tài)還是處于Background狀態(tài),收到遠(yuǎn)程推送消息的時候都會立即調(diào)用此方法。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ }
這里引入一個概念:
**推送喚醒(remote notifications)
**iOS7以前,當(dāng)你收到推送消息時,你需要先打開應(yīng)用,等待應(yīng)用從網(wǎng)絡(luò)上獲取推送的信息之后,才能將信息呈現(xiàn)出來。而iOS7改變了這一過程。當(dāng)系統(tǒng)收到推送消息時,不是首先提醒用戶,而是喚醒對應(yīng)的應(yīng)用,讓應(yīng)用在后臺獲取對應(yīng)的信息。當(dāng)信息處理完成后,再提醒用戶。一個很小的改變,但是可以很大的提升用戶體驗(yàn)。同樣,iOS系統(tǒng)也會限制這種推送消息的頻率,防止系統(tǒng)被頻繁喚醒影響續(xù)航。
此時需要更改推送的payload,如果想要使用推送來喚醒應(yīng)用運(yùn)行代碼的話,需要在payload中加入content-available,并設(shè)置為1。
aps {
content-available: 1
alert: {...}
}
1.程序在前臺(Foreground)時收到推送:
如果設(shè)置remote notifications,那么先執(zhí)行相對應(yīng)的方法,在后臺收到推送也是如此。
在前臺收到通知時,會調(diào)用下面這個方法,可以在這個方法里面實(shí)現(xiàn)收到通知時刷新或跳轉(zhuǎn)界面的功能;程序在前臺收到推送時通知欄不會彈出推送信息
-(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo{}
2.程序在后臺時收到推送:
如果設(shè)置remote notifications,那么先執(zhí)行相對應(yīng)的方法,在前臺收到推送也是如此。
如果用戶點(diǎn)擊通知欄信息進(jìn)入程序會調(diào)用情況1中的方法,所以在情況1的方法里面需要根據(jù)程序在前臺還是后臺來執(zhí)行不同操application.applicationState
3.當(dāng)程序關(guān)閉時收到推送:程序關(guān)閉時收到推送時,用戶點(diǎn)擊通知欄信息進(jìn)入應(yīng)用的時會調(diào)用
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// 在此方法中一定要調(diào)用completionHandler這個回調(diào),告訴系統(tǒng)是否處理成功
UIBackgroundFetchResultNewData, // 成功接收到數(shù)據(jù) UIBackgroundFetchResultNoData, // 沒有接收到數(shù)據(jù) UIBackgroundFetchResultFailed // 接受失敗
if (userInfo) {
completionHandler(UIBackgroundFetchResultNewData);
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
可操作通知類型收到推送消息時回調(diào)方法
// 此兩個回調(diào)方法對應(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;
這個方法可以設(shè)置應(yīng)用程序的角標(biāo)的數(shù)值。但是當(dāng)程序關(guān)閉時,收到推送后我們就不能改變角標(biāo)數(shù)值了。所以建議讓服務(wù)端推送過來的信息里加上'badge' = 88這個鍵值對來確定角標(biāo)的顯示數(shù)值。這樣程序在后臺還是關(guān)閉,只要顯示服務(wù)端傳給我們的角標(biāo)值就好了。
不過當(dāng)我們閱讀完一條消息的時需要告訴服務(wù)器,并且將[UIApplication sharedApplication].applicationIconBadgeNumber減一。
當(dāng)客戶端殺死情況走本地推送(當(dāng)客戶端開啟走—(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions)。
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
// 當(dāng)被殺死狀態(tài)收到本地通知時執(zhí)行的跳轉(zhuǎn)代碼
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NSDictionary *infoDict = [mnResource parseJSONStringToNSDictionary:notification.userInfo[@"info"]];
[self.delegate diaplayAlertWhenReceivePushInfo:infoDict];
}
到現(xiàn)在為止做iOS 推送也差不多算入行了吧(都是用的個推)
昨天調(diào)試個推送也讓我蛋疼,調(diào)試了一晚上,其他系統(tǒng)的沒什么問題,就有個手機(jī)是iOS 10.0.2的系統(tǒng),不知道為什么走不通apns。
遇到這些問題不要慌,先定為到問題。下面列舉自己踩過的個推方面的坑:
1.打包的證書與個推后臺的證書環(huán)境不一致,導(dǎo)致推送失敗。
2.服務(wù)端忘記設(shè)置了個推安卓和ios的key,沒有區(qū)分,所以會向客戶端下發(fā)兩條推送,那是我解析的時候是蒙蔽的,json解析出來怎么會有兩種key完全不同的字典。這時候alert很大幾率顯示null且不能跳轉(zhuǎn),因?yàn)槟憬馕霾怀鰜怼?br>
3.上面那種講到的情況,自己分析應(yīng)該是
這里在說明一點(diǎn)在使用個推的時候上傳個推平臺的.p12要和打包的證書環(huán)境一致哦(生產(chǎn)環(huán)境和調(diào)試環(huán)境)
參考:http://www.itdecent.cn/p/4b947569a548