push的主要工作流程:

- iOS設(shè)備連接網(wǎng)絡(luò)后,會自動與APNS保持類似TCP的長連接,等待APNS推送消息。
- 應(yīng)用啟動時會注冊消息推送,并且會從APNS獲取到注冊的唯一設(shè)備標(biāo)識deviceToken,我們要把上傳給應(yīng)用的服務(wù)器。

- 在需要給應(yīng)用推送消息時,Provider把push內(nèi)容、接受push消息的deviceToken按APNS指定的格式打包好,發(fā)送給APNS;
- APNS接收到Provider發(fā)送的消息后,查找deviceToken,如果該設(shè)備已經(jīng)和APNS建立了連接,則立即將該消息推送給該設(shè)備,如果設(shè)備不在線,則在設(shè)備下次連接到APNS后將消息推送給該設(shè)備。請注意蘋果并不保證推送一定成功。
- 設(shè)備受到push消息后,iOS系統(tǒng)會根據(jù)SSL證書判斷這個push消息是發(fā)給那個應(yīng)用的,進(jìn)而啟動相應(yīng)的客戶端。
在上述過程中,有兩個關(guān)鍵步驟需要自己處理:
- 獲取設(shè)備的deviceToken并上傳到Provider;
- Provider發(fā)送推送消息到APNS;
這兩個步驟都需要蘋果的證書授權(quán)。
iOS 10 中以前雜亂的和通知相關(guān)的 API 都被統(tǒng)一了,現(xiàn)在開發(fā)者可以使用獨(dú)立的 UserNotifications.framework 來集中管理和使用 iOS 系統(tǒng)中通知的功能。在此基礎(chǔ)上,Apple 還增加了撤回單條通知,更新已展示通知,中途修改通知內(nèi)容,在通知中展示圖片視頻,自定義通知 UI 等一系列新功能,非常強(qiáng)大。
在程序啟動后注冊通知:
if (NSClassFromString(@"UNUserNotificationCenter")) {
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError *error) {
if (granted) {
#if !TARGET_IPHONE_SIMULATOR
UIApplication *application = [UIApplication sharedApplication];
[application registerForRemoteNotifications];
#endif
}
}];
return;
}
程序第一次啟動調(diào)用這個方法時又彈出框:

要注意的是,一旦用戶選擇了不允許,之后程序里再次調(diào)用該方法也不會再進(jìn)行彈窗,granted會一直是NO,用戶必須自行前往系統(tǒng)的設(shè)置中為你的應(yīng)用打開通知才能收到推動消息。
遠(yuǎn)程推送
一旦用戶同意后,你就可以在應(yīng)用中發(fā)送本地通知了。如果你想通過服務(wù)器發(fā)送遠(yuǎn)程通知的話,還需要多一個獲取用戶 token 的操作。你的服務(wù)器可以使用這個 token 向 Apple Push Notification 的服務(wù)器提交請求,然后 APNs 通過 token 識別設(shè)備和應(yīng)用,將通知推給用戶。
我們使用 UIApplication 的 registerForRemoteNotifications 來注冊遠(yuǎn)程通知,在 AppDelegate 的 代理方法中獲取用戶 token:
UIApplication *application = [UIApplication sharedApplication];
[application registerForRemoteNotifications];
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
發(fā)送一個本地的推送:
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
content.title = @"test local push";
content.body = @"本地的推送消息";
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
NSString * requestIdentifier = @"com.onevcat.usernotification.myFirstNotification";
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@", error);
}
}];
調(diào)用這個方法以后把程序切換到后臺(程序在前臺時不會顯示本地通知):

處理通知:
//當(dāng)應(yīng)用在前臺收到通知時會調(diào)用這個方法,應(yīng)用在后臺或殺掉應(yīng)用時是不會調(diào)用的
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
//在前臺顯示通知
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);
//如果不想顯示這個通知,可以
//completionHandler(0);
}
//這個代理方法會在用戶與你推送的通知進(jìn)行交互時被調(diào)用(不論在前臺還是后臺),包括用戶通過通知打開了你的應(yīng)用,或者出發(fā)了某個action,在這個方法的里必須要調(diào)用completionHandler來告訴系統(tǒng)你已經(jīng)處理了通知
//UNNotificationResponses是一個幾乎包含了通知所有信息的對象,可以獲取到本地推送消息的userinfo,遠(yuǎn)程推送的payload內(nèi)的內(nèi)容也會出現(xiàn)在這個userInfo中??梢酝ㄟ^userInfo的內(nèi)容來決定頁面的跳轉(zhuǎn)或者是進(jìn)行其它操作。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void(^)())completionHandler {
NSLog(@"%s", __func__);
completionHandler();
}

在iOS10里,本地通知和遠(yuǎn)程通知合二為一,區(qū)分本地通知跟遠(yuǎn)程通知的類是UNPushNotificationTrigger這個類,UNPushNotificationTrigger的類型是新增加的,通過它,我們可以得到一些通知的觸發(fā)條件,在使用時,我們不應(yīng)該直接使用這個類,應(yīng)當(dāng)使用它的子類。
- UNTimeIntervalNotificationTrigger 在特定的時間后出發(fā)本地通知
UNCalendarNotificationTrigger 在特定的date觸發(fā)本地通知
UNLocationNotificationTrigger 在用戶到達(dá)特定的地理位置時出發(fā)本地通知
UNPushNotificationTrigger 表示APNS通知
// iOS 10收到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
NSDictionary * userInfo = notification.request.content.userInfo;
UNNotificationRequest *request = notification.request; // 收到推送的請求
UNNotificationContent *content = request.content; // 收到推送的消息內(nèi)容
NSNumber *badge = content.badge; // 推送消息的角標(biāo)
NSString *body = content.body; // 推送消息體
UNNotificationSound *sound = content.sound; // 推送消息的聲音
NSString *subtitle = content.subtitle; // 推送消息的副標(biāo)題
NSString *title = content.title; // 推送消息的標(biāo)題
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 前臺收到遠(yuǎn)程通知:%@", [self logDic:userInfo]);
}
else {
// 判斷為本地通知
NSLog(@"iOS10 前臺收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
}
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert); // 需要執(zhí)行這個方法,選擇是否提醒用戶,有Badge、Sound、Alert三種類型可以設(shè)置
}
通知的點(diǎn)擊事件
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void(^)())completionHandler{
NSDictionary * userInfo = response.notification.request.content.userInfo;
UNNotificationRequest *request = response.notification.request; // 收到推送的請求
UNNotificationContent *content = request.content; // 收到推送的消息內(nèi)容
NSNumber *badge = content.badge; // 推送消息的角標(biāo)
NSString *body = content.body; // 推送消息體
UNNotificationSound *sound = content.sound; // 推送消息的聲音
NSString *subtitle = content.subtitle; // 推送消息的副標(biāo)題
NSString *title = content.title; // 推送消息的標(biāo)題
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 收到遠(yuǎn)程通知:%@", [self logDic:userInfo]);
}
else {
// 判斷為本地通知
NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
}
// Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.
completionHandler(); // 系統(tǒng)要求執(zhí)行這個方法
