iOS10推送通知整理總結(jié)

這篇文章整理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è)類型福侈。
  • 額外操作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:

新通知框架的兩個(gè)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è)文件夾:


    Notification Service Extension

    在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è)文件夾:


NotificationContent Extension
  • 文件夾中有:
    控制器
    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:

demo

感謝閱讀
你的支持是我寫作的唯一動力

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胎许,一起剝皮案震驚了整個(gè)濱河市峻呛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辜窑,老刑警劉巖钩述,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異穆碎,居然都是意外死亡牙勘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門所禀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來方面,“玉大人,你說我怎么就攤上這事色徘」Ы穑” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵褂策,是天一觀的道長横腿。 經(jīng)常有香客問我,道長斤寂,這世上最難降的妖魔是什么耿焊? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮遍搞,結(jié)果婚禮上罗侯,老公的妹妹穿的比我還像新娘。我一直安慰自己溪猿,他們只是感情好钩杰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诊县,像睡著了一般榜苫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翎冲,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天垂睬,我揣著相機(jī)與錄音,去河邊找鬼。 笑死驹饺,一個(gè)胖子當(dāng)著我的面吹牛钳枕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赏壹,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼鱼炒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝌借?” 一聲冷哼從身側(cè)響起昔瞧,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菩佑,沒想到半個(gè)月后自晰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稍坯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年酬荞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞧哟。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡混巧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勤揩,到底是詐尸還是另有隱情咧党,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布陨亡,位于F島的核電站傍衡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏数苫。R本人自食惡果不足惜聪舒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一辨液、第九天 我趴在偏房一處隱蔽的房頂上張望虐急。 院中可真熱鬧,春花似錦滔迈、人聲如沸止吁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敬惦。三九已至,卻和暖如春谈山,著一層夾襖步出監(jiān)牢的瞬間俄删,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畴椰,地道東北人臊诊。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像斜脂,于是被迫代替她去往敵國和親抓艳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容