這篇文章整理iOS10之后的推送通知(文中的推送通知,如不做特殊說明,默認(rèn)是iOS10以后的推送通知)
iOS10之前的推送通知,請看這篇
(本文用到的遠(yuǎn)程推送工具PushMeBaby,也在上篇文章中)
在iOS10上
蘋果將原來散落在UIKit中各處的用戶通知相關(guān)的代碼進(jìn)行重構(gòu)物遇,剝離
打造了一個(gè)全新的通知框架-UserNotifications
因公司業(yè)務(wù)需求,對這方面做了一些研究
總結(jié)此文
1.二話不說,先發(fā)一條最簡單的通知,然后逐步介紹
從本地通知開始:
//注冊通知:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[self createNotificationCategoryActions]];
// 必須寫代理启绰,不然無法監(jiān)聽通知的接收與點(diǎn)擊
center.delegate = self;
//判斷當(dāng)前注冊狀態(tài)
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus==UNAuthorizationStatusNotDetermined) {
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
}];
}
}];
return YES;
}
//添加category:
-(NSSet *)createNotificationCategoryActions{
//注冊本地通知用到的Action
//進(jìn)入app按鈕
UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:@"localAction" title:@"處理本地通知" options:UNNotificationActionOptionForeground];
///回復(fù)文本按鈕
UNTextInputNotificationAction * localText = [UNTextInputNotificationAction actionWithIdentifier:@"localText" title:@"本地文本" options:UNNotificationActionOptionNone];
//取消按鈕
UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:@"localCancel" title:@"取消" options:UNNotificationActionOptionDestructive];
//將這些action帶入category
UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:@"localCategory" actions:@[localAction,localText,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
return [NSSet setWithObjects:remoteCategory,localCategory,nil];
}
//發(fā)送本地通知 :
- (IBAction)pushNotifacation:(UIButton *)sender {
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc]init];
notificationContent.title = @"iOS10本地通知";
notificationContent.subtitle = @"扶我起來";
notificationContent.body = @"我要寫代碼";
notificationContent.badge = @1;
notificationContent.userInfo = @{@"content" : @"我是userInfo"};
//添加音效
UNNotificationSound *sound = [UNNotificationSound soundNamed:@"caodi.m4a"];
notificationContent.sound = sound;
//添加觸發(fā)器
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:2.0 repeats:NO];
NSString *identifer = @"identifer";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifer content:notificationContent trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"發(fā)送成功");
}
}];
}
-
效果圖:
接下來介紹代碼中的每一步:
-
注冊權(quán)限:
iOS10中添加了新接口,可以判斷當(dāng)前推送通知的授權(quán)狀態(tài):
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus==UNAuthorizationStatusNotDetermined) {
//判斷當(dāng)沒有拿到權(quán)限的時(shí)候,開始注冊推送通知權(quán)限
//注冊成功以后,如果需要遠(yuǎn)程通知,則注冊deviceToken
}
}];
-
添加推送聲音:
UNNotificationSound *sound = [UNNotificationSound soundNamed:@"caodi.m4a"];
notificationContent.sound = sound;
-
添加附件:
NSString *imageFile = [[NSBundle mainBundle]pathForResource:@"sport" ofType:@"png"];
UNNotificationAttachment *image = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:imageFile] options:nil error:nil];
notificationContent.attachments = @[image];
iOS10之前通知的樣式不能更改
在iOS10之后引入了UNNotificationationAttachment
可以在通知中添加圖片管怠,音頻横侦,視頻
蘋果對這些附件的大小和類型有一個(gè)限制:
上文中的代碼沒有添加附件,這里附上效果圖:
關(guān)于創(chuàng)建附件方法中的options,請參考大神徐不同作者的文章
-
添加觸發(fā)器:UNNotificationTrigger , UNNotificationTrigger有四個(gè)子類:
- UNPushNotificationTrigger:
遠(yuǎn)程推送觸發(fā)器畔派,一般是遠(yuǎn)程推送推過來的通知帶有這類觸發(fā)器 - UNTimeIntervalNotificationTrigger:
時(shí)間間隔觸發(fā)器沼头,定時(shí)或者是重復(fù),在本地推送設(shè)置中有用 - UNCalendarNotificationTrigger:
日歷觸發(fā)器鲤屡,指定日期進(jìn)行通知 - UNLocationNotificationTrigger:
地理位置觸發(fā)器,指定觸發(fā)通知的條件是地理位置CLRegion這個(gè)類型福侈。
- UNPushNotificationTrigger:
-
額外操作Category:
和iOS10之前一樣
點(diǎn)擊通知可以進(jìn)行快捷回復(fù)
回復(fù)操作以組的形式在注冊通知的時(shí)候添加:
//注冊本地通知用到的Action
//進(jìn)入app按鈕
UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:@"localAction" title:@"處理本地通知" options:UNNotificationActionOptionForeground];
///回復(fù)文本按鈕
UNTextInputNotificationAction * localText = [UNTextInputNotificationAction actionWithIdentifier:@"localText" title:@"本地文本" options:UNNotificationActionOptionNone];
//取消按鈕
UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:@"localCancel" title:@"取消" options:UNNotificationActionOptionDestructive];
//將這些action帶入category
UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:@"localCategory" actions:@[localAction,localText,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
UNNotificationActionOptions是一個(gè)枚舉:
UNNotificationActionOptionDestructive : 破壞性的,顯示為紅色
UNNotificationActionOptionNone : 不必打開app進(jìn)行操作
UNNotificationActionOptionForeground : 打開app進(jìn)行操作
-
創(chuàng)建通知并添加到通知中心:
觸發(fā)器和通知內(nèi)容內(nèi)容最后形成UNNotificationRequest
直接交給通知中心進(jìn)行發(fā)送
發(fā)送成功后
該通知會按照觸發(fā)器的觸發(fā)條件進(jìn)行觸發(fā)
并且會顯示到通知中心上
用戶可與指定的category交互方式與通知進(jìn)行交互
-
以上就是iOS10的通知中心注冊和設(shè)置管理的過程执俩,一下還有一些比較有用API:
//獲取在Pending狀態(tài)下待觸發(fā)的通知
- (void)getPendingNotificationRequestsWithCompletionHandler:(void(^)(NSArray<UNNotificationRequest *> *requests))completionHandler;
//移除未觸發(fā)的通知
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray<NSString *> *)identifiers;
- (void)removeAllPendingNotificationRequests;
// 通知已經(jīng)觸發(fā),但是還在操作系統(tǒng)的通知中心上癌刽,可以進(jìn)行查詢和刪除
- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray<UNNotification *> *notifications))completionHandler __TVOS_PROHIBITED;
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray<NSString *> *)identifiers __TVOS_PROHIBITED;
- (void)removeAllDeliveredNotifications __TVOS_PROHIBITED;
-
iOS遠(yuǎn)程通知:
遠(yuǎn)程通知與本地通知的流程一樣
只不過觸發(fā)器是UNPushNotificationTrigger
并且不需要形成request
由Provider Service發(fā)送給APNs
APNs發(fā)送給蘋果設(shè)備以后生成
在代理回調(diào)的函數(shù)中獲取request
2.通知的監(jiān)聽:
蘋果對iOS10中的通知監(jiān)聽方法進(jìn)行了整合
并且把收到通知和點(diǎn)擊通知分開來處理:
app在前臺的時(shí)候,收到了通知
這時(shí)候 app 不會彈 alert
但是還是會走回調(diào) :
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
/** 根據(jù)觸發(fā)器類型 來判斷通知類型 */
if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
//遠(yuǎn)程通知處理
NSLog(@"收到遠(yuǎn)程通知");
}else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
//時(shí)間間隔觸發(fā)器通知處理
NSLog(@"收到本地通知");
}else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
//日歷觸發(fā)器通知處理
NSLog(@"收到本地通知");
}else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
//位置觸發(fā)器通知處理
NSLog(@"收到本地通知");
}
/** 如果不想按照系統(tǒng)的方式展示通知,可以不傳入U(xiǎn)NNotificationPresentationOptionAlert,自定義彈窗 */
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
}
app在后臺收到通知
并且點(diǎn)擊的時(shí)候調(diào)用 :
//用戶與通知進(jìn)行交互后的response役首,比如說用戶直接點(diǎn)開通知打開App、用戶點(diǎn)擊通知的按鈕或者進(jìn)行輸入文本框的文本
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{
//在此显拜,可判斷response的種類和request的觸發(fā)器是什么衡奥,可根據(jù)遠(yuǎn)程通知和本地通知分別處理,再根據(jù)action進(jìn)行后續(xù)回調(diào)
if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {//遠(yuǎn)程通知
//可根據(jù)actionIdentifier來做業(yè)務(wù)邏輯
if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
UNTextInputNotificationResponse * textResponse = (UNTextInputNotificationResponse*)response;
NSString * text = textResponse.userText;
NSLog(@"回復(fù)內(nèi)容 : %@",text);
}else{
if ([response.actionIdentifier isEqualToString:@"remoteAction"]) {
NSLog(@"點(diǎn)擊了處理遠(yuǎn)程通知按鈕");
}
}
}else {//本地通知
//可根據(jù)actionIdentifier來做業(yè)務(wù)邏輯
if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
UNTextInputNotificationResponse * textResponse = (UNTextInputNotificationResponse*)response;
NSString * text = textResponse.userText;
NSLog(@"回復(fù)內(nèi)容 : %@",text);
}else{
if ([response.actionIdentifier isEqualToString:@"localAction"]) {
NSLog(@"點(diǎn)擊了處理本地通知按鈕");
}
}
}
completionHandler();
}
在 iOS10 之前
如果 app 是殺死狀態(tài)
這個(gè)時(shí)候點(diǎn)擊 push 進(jìn)入 app
這時(shí)候并不走 appDelegate 的通知處理方法
而是在 LaunchingWithOptions 方法中的參數(shù)launchOptions中有一個(gè) key 不為空
let local = launchOptions![UIApplicationLaunchOptionsKey.localNotification]
在 iOS10 以后
把 app 殺死的情況和活躍在后臺的情況統(tǒng)一了
都是走 appDelegate 的通知處理方法
但是 iOS10 以后 LaunchingWithOptions 方法中的參數(shù) launchOptions 的哪個(gè) key, 依然不為空
3.Extension:
-
3.1Notification Service Extension:
- 使得推送的數(shù)據(jù)在iOS系統(tǒng)展示之前远荠,經(jīng)過App開發(fā)者的Extension矮固,可以在不啟動App的情況下,完成一些快捷操作邏輯
- 雖然iOS10的推送數(shù)據(jù)包已經(jīng)達(dá)到4k,但是對于一些圖片視頻gif還是無力的档址,有了Extension盹兢,可以在此下載完畢然后直接展示,豐富的圖片和視頻可以在此顯示
在XCode菜單中選擇File->New->Target,創(chuàng)建Notification Service Extension
創(chuàng)建Extension之后,項(xiàng)目中多了這個(gè)文件夾:
在NotificationService中有兩個(gè)方法:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;
- (void)serviceExtensionTimeWillExpire;
在Demo中,重寫了第一個(gè)方法
在這個(gè)方法中
可以拿到通知
對通知進(jìn)行一系列修改
然后回調(diào)給系統(tǒng)
讓系統(tǒng)展示
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
// copy發(fā)來的通知守伸,開始做一些處理
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
// 重寫一些東西
self.bestAttemptContent.title = @"我是標(biāo)題";
self.bestAttemptContent.subtitle = @"我是子標(biāo)題";
self.bestAttemptContent.body = @"我要寫代碼";
// 這里添加一些點(diǎn)擊事件绎秒,可以在收到通知的時(shí)候,添加尼摹,也可以在攔截通知的這個(gè)擴(kuò)展中添加
self.bestAttemptContent.categoryIdentifier = @"remoteCategory";
//進(jìn)行一系列自己的定制操作后 回調(diào)給系統(tǒng):
self.contentHandler(self.bestAttemptContent);
}
需要注意的是
如果創(chuàng)建了Notification Service Extension
并使用它
在推送體中必須加mutable-content字段
Notification Service Extension暫時(shí)只支持遠(yuǎn)程推送
{
"aps": {
"alert": "This is some fancy message.",
"badge": 1,
"sound": "default",
"mutable-content": "1"
}
}
-
3.2NotificationContent Extension:
在XCode菜單中選擇File->New->Target,創(chuàng)建NotificationContent Extension
創(chuàng)建成功以后
項(xiàng)目中也多了一個(gè)文件夾:
- 文件夾中有:
控制器
storyboard
info.plist-
先看info.plist:
這個(gè)字段是需要我們修改的:
1:UNNotificationExtensionCategory:
對應(yīng)這個(gè)key的值见芹,可以是一個(gè)字符串,也可以是一個(gè)數(shù)組,每一個(gè)字符串都是一個(gè)identifier,這個(gè)identifier對應(yīng)著每一個(gè)UNMutableNotificationContent的categoryIdentifier的屬性,category在我們注冊通知的時(shí)候,已經(jīng)寫好.我們可以根據(jù)收到的通知中字段的不同,來顯示不同的操作按鈕.
2:UNNotificationExtensionInitialContentSizeRatio:
這個(gè)值的類型是一個(gè)浮點(diǎn)類型蠢涝,代表的是高度與寬度的比值玄呛。系統(tǒng)會使用這個(gè)比值,作為初始化view的大小和二。舉個(gè)簡單的例子來說徘铝,如果該值為1,則該視圖為正方形惯吕。如果為0.5惕它,則代表高度是寬度的一半。
注意這個(gè)值只是初始化的一個(gè)值混埠,在這個(gè)擴(kuò)展添加后,可以重寫frame诗轻,展示的時(shí)候钳宪,在我們還沒打開這個(gè)視圖預(yù)覽時(shí),背景是個(gè)類似圖片占位的灰色扳炬,那個(gè)灰色的高度寬度之比吏颖,就是通過這個(gè)值來設(shè)定。
3.UNNotificationExtensionDefaultContentHidden.
這個(gè)值是一個(gè)BOOL值恨樟,當(dāng)為YES時(shí)半醉,會隱藏上方原本推送的內(nèi)容視圖,只會顯示我們自定義的視圖劝术。(因?yàn)樵谧远x視圖的時(shí)候缩多,我們可以取得推送內(nèi)容,然后按照我們想要的布局养晋,展示出來)如果為NO時(shí)(默認(rèn)為NO)衬吆,推送視圖就會既有我們的自定義視圖,也會有系統(tǒng)原本的推送內(nèi)容視圖
4.至于NSExtensionMainStoryboard以及NSExtensionPointIdentifier绳泉,系統(tǒng)默認(rèn)生成逊抡,大家直接用就好 -
然后文件夾中還有控制器和storyboard:
這部分你可以自定義UI,注意的是該視圖控制器無法響應(yīng)交互控件零酪,要想使用交互組件冒嫡,就必須配合UNNotificationAction和category來對應(yīng)你的UI部分拇勃,還有一點(diǎn),Notification Content Extension只能有一個(gè)控制器孝凌,所以你要想定制多種UI方咆,就需要代碼判斷加載不同的View來實(shí)現(xiàn)。
通過自定義試圖后收到通知的效果 :
圖中綠色部分,就是在storyboard中自定義的view.
-
本文Demo:
感謝閱讀
你的支持是我寫作的唯一動力