[TOC]
一、工作機制:
APNS 是 Apple Push Notification Service 的縮寫,是蘋果服務器。

image
工作流程如下所示:
- 首先app向iOS注冊推送消息
- iOS收到app的注冊后,向APNS Server索要deviceToken,app接收返回的deviceToken
- app把接收到的deviceToken傳給我們的服務器
- 當我們的服務器需要推送消息時,就把要推送的消息和deviceToken發(fā)送給APNS Server
- APNS Server服務將推送的消息發(fā)送給app
二、app代碼實現(xiàn):
1、注冊消息推送:
#import <UserNotifications/UserNotifications.h>
// 注冊APNS推送
- (void)registerRemoteNotification {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
// 必須寫代理,不然無法監(jiān)聽通知的接收與點擊事件
notificationCenter.delegate = self;
// 請求授權
[notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted && !error) {
// 用戶同意授權,注冊遠程推送
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// 用戶不同意授權
}
}];
// 獲取用戶同意的或者更改的推送權限設置
[notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"%@",settings);
}];
} else {
// iOS8 ~ iOS10
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
}
}
}
2、把拿到到deviceToken傳給我們的服務器:
// 獲取deviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// 把deviceToken轉為NSString
NSString *deviceStr = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
deviceStr = [deviceStr stringByReplacingOccurrencesOfString:@" " withString:@""];
// 把deviceToken傳給我們自己的服務器
}
// 獲取deviceToken失敗
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"error:%@",error.description);
}
3、app接收到遠程推送的消息:
#pragma mark iOS10 收到通知(本地和遠程)
// App接收通知時
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的請求
UNNotificationRequest *request = notification.request;
// 收到推送的內容
UNNotificationContent *content = request.content;
// 收到推送的基本信息
NSDictionary *userInfo = content.userInfo;
// 收到推送消息的角標
NSNumber *badge = content.badge;
// 推送消息的聲音
UNNotificationSound *sound = content.sound;
// 推送消息的副標題
NSString *subtitle = content.subtitle;
// 推送消息的標題
NSString *title = content.title;
// 推送消息的類型
if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
// 遠程通知
} else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
// 本地通知,一定時間之后,重復或者不重復推送通知 我們可以設置timeInterval(時間間隔)和repeats(是否重復)。
} else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
// 本地通知,一定日期之后,重復或者不重復推送通知 例如,你每天8點推送一個通知,只要dateComponents為8,如果你想每天8點都推送這個通知,只要repeats為YES就可以了
} else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
// 本地通知,地理位置的一種通知,當用戶進入或離開一個地理區(qū)域來通知。
}
// 執(zhí)行下面方法,選擇提醒用戶的方式
completionHandler(UNNotificationPresentationOptionBadge|
UNNotificationPresentationOptionSound|
UNNotificationPresentationOptionAlert);
}
// App通知的點擊事件,只會是用戶點擊消息才會觸發(fā),如果使用戶長按(3DTouch)、Action等并不會觸發(fā)。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的請求
UNNotificationRequest *request = response.notification.request;
// 。。。
// 系統(tǒng)要求執(zhí)行這個方法
completionHandler();
}
#pragma mark iOS10 之前收到通知
// iOS10以下收到本地推送通知
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
}
// iOS7及以上收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
}
// iOS6及以下收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
三、本地推送:
本地推送這里主要分兩個版本來介紹,一個是iOS 10及以后,一個是iOS 10以前;
1、iOS 10及以上本地推送生成流程:
創(chuàng)建一個觸發(fā)器 trigger
創(chuàng)建推送的內容 UNMutableNotificationContent
創(chuàng)建推送請求 UNNotificationRequest
-
推送請求添加到推送管理中心 UNUserNotificationCenter
- (void)postLocalNotificatonUpper10 API_AVAILABLE(ios(10.0)) { // 1. 創(chuàng)建一個觸發(fā)器(trigger),這里以 UNTimeIntervalNotificationTrigger 為例 UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:30 repeats:NO]; // 2. 創(chuàng)建推送的內容 UNMutableNotificationContent UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"title"; content.subtitle = @"subtitle"; content.body = @"body"; content.badge = @2; content.sound = [UNNotificationSound defaultSound]; content.userInfo = @{@"key1" : @"value1", @"key2" : @"value2"}; // // 推送交互操作 // content.categoryIdentifier = @"Dely_locationCategory"; // [self addNotificationAction]; // 3. 創(chuàng)建推送請求 UNNotificationRequest UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"identifier" content:content trigger:timeTrigger]; // 4. 推送請求添加到推送管理中心 UNUserNotificationCenter UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (!error) { NSLog(@"推送已添加成功"); } }]; }
2、iOS 10以下本地推送生成流程:
- 創(chuàng)建本地通知對象 UILocalNotification
- 把本地通知對象加入到app日程表中
- (void)postLocalNotificatonBelow10 {
/*
fireDate:啟動時間
timeZone:啟動時間參考的時區(qū)
repeatInterval:重復推送時間(NSCalendarUnit類型),0代表不重復
repeatCalendar:重復推送時間(NSCalendar類型)
alertBody:通知內容
alertAction:解鎖滑動時的事件
alertLaunchImage:啟動圖片,設置此字段點擊通知時會顯示該圖片
alertTitle:通知標題,適用iOS8.2之后
applicationIconBadgeNumber:收到通知時App icon的角標
soundName:推送是帶的聲音提醒,設置默認的字段為UILocalNotificationDefaultSoundName
userInfo:發(fā)送通知時附加的內容
category:此屬性和注冊通知類型時有關聯(lián),(有興趣的同學自己了解,不詳細敘述)適用iOS8.0之后
region:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之后
regionTriggersOnce:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之后
*/
// 1. 創(chuàng)建本地通知對象 UILocalNotification
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:30];
localNotification.alertBody = @"通知顯示內容";
localNotification.alertAction = @"解鎖滑動是的事件";
localNotification.applicationIconBadgeNumber = 1;
localNotification.soundName = UILocalNotificationDefaultSoundName;
// 2. 把本地通知對象加入到app日程表中
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// 立即發(fā)送通知
// [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
四、iOS 10推送交互操作:
iOS 10中,可以允許推送添加交互操作 action,這些 action 使得app可以在前臺或者后臺執(zhí)行一些邏輯代碼。這是推送功能的一個拓展,可通過3D-Touch觸發(fā),或者右滑會出現(xiàn)view和clear選項來觸發(fā)。
- 創(chuàng)建action
- 創(chuàng)建category
- 把category添加到通知中心
// 添加推送交互操作
- (void)addNotificationAction API_AVAILABLE(ios(10.0)) {
/*
1.需要解鎖顯示,點擊不會進app
UNNotificationActionOptionAuthenticationRequired
2.點擊不會進app
UNNotificationActionOptionDestructive
3.點擊會進app
UNNotificationActionOptionForeground
*/
// 1. 創(chuàng)建action
UNNotificationAction *lookAction =
[UNNotificationAction actionWithIdentifier:@"action.join"
title:@"接收邀請"
options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction *joinAction =
[UNNotificationAction actionWithIdentifier:@"action.look"
title:@"查看邀請"
options:UNNotificationActionOptionForeground];
UNNotificationAction *cancelAction =
[UNNotificationAction actionWithIdentifier:@"action.cancel"
title:@"取消"
options:UNNotificationActionOptionDestructive];
// 2. 創(chuàng)建category
// * identifier 標識符
// * actions 操作數(shù)組
// * intentIdentifiers 意圖標識符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是針對電話、carplay 等開放的 API。
// * options 通知選項 枚舉類型 也是為了支持 carplay
UNNotificationCategory *category =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[lookAction, joinAction, cancelAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
// 3. 把category添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
}
// 添加推送文本輸入交互操作
- (void)addTextNotificationAction API_AVAILABLE(ios(10.0)) {
// 創(chuàng)建 UNTextInputNotificationAction 比 UNNotificationAction 多了兩個參數(shù)
// * buttonTitle 輸入框右邊的按鈕標題
// * placeholder 輸入框占位符
UNTextInputNotificationAction *inputAction =
[UNTextInputNotificationAction actionWithIdentifier:@"action.input"
title:@"輸入"
options:UNNotificationActionOptionForeground
textInputButtonTitle:@"發(fā)送"
textInputPlaceholder:@"tell me loudly"];
// 注冊 category
UNNotificationCategory *notificationCategory =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[inputAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:notificationCategory]];
}
- 事件的操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// iOS 10推送交互事件的操作,在這里處理
//點擊或輸入action
NSString* actionIdentifierStr = response.actionIdentifier;
//輸入
if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
NSString* userSayStr = [(UNTextInputNotificationResponse *)response userText];
}
//點擊
if ([actionIdentifierStr isEqualToString:@"action.join"]) {
} else if ([actionIdentifierStr isEqualToString:@"action.look"]) {
}
// 系統(tǒng)要求執(zhí)行這個方法
completionHandler();
}
注意,遠程推送一定要保證 category 的鍵值對是一致的
{
"aps" : {
"alert" : {
"title" : "iOS遠程消息,我是主標題!-title",
"subtitle" : "iOS遠程消息,我是主標題!-Subtitle",
"body" : "Dely,why am i so handsome -body"
},
"category" : "Dely_locationCategory",
"badge" : "2"
}
}