iOS 消息推送

[TOC]

一杆融、工作機(jī)制:

APNS 是 Apple Push Notification Service 的縮寫(xiě)褥紫,是蘋(píng)果服務(wù)器亿扁。

image

工作流程如下所示:

  • 首先app向iOS注冊(cè)推送消息
  • iOS收到app的注冊(cè)后如蚜,向APNS Server索要deviceToken择镇,app接收返回的deviceToken
  • app把接收到的deviceToken傳給我們的服務(wù)器
  • 當(dāng)我們的服務(wù)器需要推送消息時(shí)挡逼,就把要推送的消息和deviceToken發(fā)送給APNS Server
  • APNS Server服務(wù)將推送的消息發(fā)送給app

二、app代碼實(shí)現(xiàn):

1腻豌、注冊(cè)消息推送:

#import <UserNotifications/UserNotifications.h>

// 注冊(cè)APNS推送
- (void)registerRemoteNotification {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
        // 必須寫(xiě)代理家坎,不然無(wú)法監(jiān)聽(tīng)通知的接收與點(diǎn)擊事件
        notificationCenter.delegate = self;
        // 請(qǐng)求授權(quán)
        [notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert
                                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                              if (granted && !error) {
                                                  // 用戶(hù)同意授權(quán),注冊(cè)遠(yuǎn)程推送
                                                  [[UIApplication sharedApplication] registerForRemoteNotifications];
                                              } else {
                                                  // 用戶(hù)不同意授權(quán)
                                              }
                                          }];
        // 獲取用戶(hù)同意的或者更改的推送權(quán)限設(shè)置
        [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傳給我們的服務(wù)器:

// 獲取deviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    
    // 把deviceToken轉(zhuǎn)為NSString
    NSString *deviceStr = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceStr = [deviceStr stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    // 把deviceToken傳給我們自己的服務(wù)器
    
}

// 獲取deviceToken失敗
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"error:%@",error.description);
}

3虱疏、app接收到遠(yuǎn)程推送的消息:

#pragma mark iOS10 收到通知(本地和遠(yuǎn)程)

// App接收通知時(shí)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  API_AVAILABLE(ios(10.0)){
    // 收到推送的請(qǐng)求
    UNNotificationRequest *request = notification.request;
    // 收到推送的內(nèi)容
    UNNotificationContent *content = request.content;
    // 收到推送的基本信息
    NSDictionary *userInfo = content.userInfo;
    // 收到推送消息的角標(biāo)
    NSNumber *badge = content.badge;
    // 推送消息的聲音
    UNNotificationSound *sound = content.sound;
    // 推送消息的副標(biāo)題
    NSString *subtitle = content.subtitle;
    // 推送消息的標(biāo)題
    NSString *title = content.title;
    // 推送消息的類(lèi)型
    if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        // 遠(yuǎn)程通知
    } else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
        // 本地通知,一定時(shí)間之后苏携,重復(fù)或者不重復(fù)推送通知 我們可以設(shè)置timeInterval(時(shí)間間隔)和repeats(是否重復(fù))做瞪。
    } else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
        // 本地通知,一定日期之后右冻,重復(fù)或者不重復(fù)推送通知 例如装蓬,你每天8點(diǎn)推送一個(gè)通知,只要dateComponents為8纱扭,如果你想每天8點(diǎn)都推送這個(gè)通知矛物,只要repeats為YES就可以了
    } else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
        // 本地通知,地理位置的一種通知跪但,當(dāng)用戶(hù)進(jìn)入或離開(kāi)一個(gè)地理區(qū)域來(lái)通知履羞。
    }
    
    // 執(zhí)行下面方法,選擇提醒用戶(hù)的方式
    completionHandler(UNNotificationPresentationOptionBadge|
                      UNNotificationPresentationOptionSound|
                      UNNotificationPresentationOptionAlert);
}

// App通知的點(diǎn)擊事件屡久,只會(huì)是用戶(hù)點(diǎn)擊消息才會(huì)觸發(fā)忆首,如果使用戶(hù)長(zhǎng)按(3DTouch)、Action等并不會(huì)觸發(fā)被环。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    // 收到推送的請(qǐng)求
    UNNotificationRequest *request = response.notification.request;
    
    // 糙及。。筛欢。
    
    // 系統(tǒng)要求執(zhí)行這個(gè)方法
    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 {
    
}

三浸锨、本地推送:

本地推送這里主要分兩個(gè)版本來(lái)介紹,一個(gè)是iOS 10及以后版姑,一個(gè)是iOS 10以前柱搜;

1、iOS 10及以上本地推送生成流程:

  • 創(chuàng)建一個(gè)觸發(fā)器 trigger

  • 創(chuàng)建推送的內(nèi)容 UNMutableNotificationContent

  • 創(chuàng)建推送請(qǐng)求 UNNotificationRequest

  • 推送請(qǐng)求添加到推送管理中心 UNUserNotificationCenter

    - (void)postLocalNotificatonUpper10 API_AVAILABLE(ios(10.0)) {
        
        // 1. 創(chuàng)建一個(gè)觸發(fā)器(trigger),這里以 UNTimeIntervalNotificationTrigger 為例
        UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:30 repeats:NO];
        
        // 2. 創(chuàng)建推送的內(nèi)容 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)建推送請(qǐng)求 UNNotificationRequest
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"identifier"
                                                                              content:content
                                                                              trigger:timeTrigger];
        
        // 4. 推送請(qǐng)求添加到推送管理中心 UNUserNotificationCenter
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center addNotificationRequest:request
                 withCompletionHandler:^(NSError * _Nullable error) {
                     if (!error) {
                         NSLog(@"推送已添加成功");
                     }
                 }];
    }
    

2剥险、iOS 10以下本地推送生成流程:

  • 創(chuàng)建本地通知對(duì)象 UILocalNotification
  • 把本地通知對(duì)象加入到app日程表中
- (void)postLocalNotificatonBelow10 {
    /*
     fireDate:?jiǎn)?dòng)時(shí)間
     timeZone:?jiǎn)?dòng)時(shí)間參考的時(shí)區(qū)
     repeatInterval:重復(fù)推送時(shí)間(NSCalendarUnit類(lèi)型)聪蘸,0代表不重復(fù)
     repeatCalendar:重復(fù)推送時(shí)間(NSCalendar類(lèi)型)
     alertBody:通知內(nèi)容
     alertAction:解鎖滑動(dòng)時(shí)的事件
     alertLaunchImage:?jiǎn)?dòng)圖片,設(shè)置此字段點(diǎn)擊通知時(shí)會(huì)顯示該圖片
     alertTitle:通知標(biāo)題,適用iOS8.2之后
     applicationIconBadgeNumber:收到通知時(shí)App icon的角標(biāo)
     soundName:推送是帶的聲音提醒健爬,設(shè)置默認(rèn)的字段為UILocalNotificationDefaultSoundName
     userInfo:發(fā)送通知時(shí)附加的內(nèi)容
     category:此屬性和注冊(cè)通知類(lèi)型時(shí)有關(guān)聯(lián)控乾,(有興趣的同學(xué)自己了解,不詳細(xì)敘述)適用iOS8.0之后
     
     region:帶有定位的推送相關(guān)屬性娜遵,具體使用見(jiàn)下面【帶有定位的本地推送】適用iOS8.0之后
     regionTriggersOnce:帶有定位的推送相關(guān)屬性蜕衡,具體使用見(jiàn)下面【帶有定位的本地推送】適用iOS8.0之后
     */
    
    // 1. 創(chuàng)建本地通知對(duì)象 UILocalNotification
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:30];
    localNotification.alertBody = @"通知顯示內(nèi)容";
    localNotification.alertAction = @"解鎖滑動(dòng)是的事件";
    localNotification.applicationIconBadgeNumber = 1;
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    
    // 2. 把本地通知對(duì)象加入到app日程表中
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    
    // 立即發(fā)送通知
//    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
    
}

四、iOS 10推送交互操作:

iOS 10中设拟,可以允許推送添加交互操作 action衷咽,這些 action 使得app可以在前臺(tái)或者后臺(tái)執(zhí)行一些邏輯代碼。這是推送功能的一個(gè)拓展蒜绽,可通過(guò)3D-Touch觸發(fā)镶骗,或者右滑會(huì)出現(xiàn)view和clear選項(xiàng)來(lái)觸發(fā)。

  • 創(chuàng)建action
  • 創(chuàng)建category
  • 把category添加到通知中心
// 添加推送交互操作
- (void)addNotificationAction API_AVAILABLE(ios(10.0)) {
    /*
     1.需要解鎖顯示躲雅,點(diǎn)擊不會(huì)進(jìn)app
     UNNotificationActionOptionAuthenticationRequired
     2.點(diǎn)擊不會(huì)進(jìn)app
     UNNotificationActionOptionDestructive
     3.點(diǎn)擊會(huì)進(jìn)app
     UNNotificationActionOptionForeground
     */
    // 1. 創(chuàng)建action
    UNNotificationAction *lookAction =
    [UNNotificationAction actionWithIdentifier:@"action.join"
                                         title:@"接收邀請(qǐng)"
                                       options:UNNotificationActionOptionAuthenticationRequired];
    UNNotificationAction *joinAction =
    [UNNotificationAction actionWithIdentifier:@"action.look"
                                         title:@"查看邀請(qǐng)"
                                       options:UNNotificationActionOptionForeground];
    UNNotificationAction *cancelAction =
    [UNNotificationAction actionWithIdentifier:@"action.cancel"
                                         title:@"取消"
                                       options:UNNotificationActionOptionDestructive];
    
    // 2. 創(chuàng)建category
    // * identifier 標(biāo)識(shí)符
    // * actions 操作數(shù)組
    // * intentIdentifiers 意圖標(biāo)識(shí)符 可在 <Intents/INIntentIdentifiers.h> 中查看鼎姊,主要是針對(duì)電話、carplay 等開(kāi)放的 API相赁。
    // * options 通知選項(xiàng) 枚舉類(lèi)型 也是為了支持 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 多了兩個(gè)參數(shù)
    // * buttonTitle 輸入框右邊的按鈕標(biāo)題
    // * placeholder 輸入框占位符
    UNTextInputNotificationAction *inputAction =
    [UNTextInputNotificationAction actionWithIdentifier:@"action.input"
                                                  title:@"輸入"
                                                options:UNNotificationActionOptionForeground
                                   textInputButtonTitle:@"發(fā)送"
                                   textInputPlaceholder:@"tell me loudly"];
    
    // 注冊(cè) 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推送交互事件的操作相寇,在這里處理
    //點(diǎn)擊或輸入action
    NSString* actionIdentifierStr = response.actionIdentifier;
    
    //輸入
    if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
        NSString* userSayStr = [(UNTextInputNotificationResponse *)response userText];
    }
    
    //點(diǎn)擊
    if ([actionIdentifierStr isEqualToString:@"action.join"]) {
    } else if ([actionIdentifierStr isEqualToString:@"action.look"]) {
    }
    
    
    // 系統(tǒng)要求執(zhí)行這個(gè)方法
    completionHandler();
}

注意,遠(yuǎn)程推送一定要保證 category 的鍵值對(duì)是一致的

{
  "aps" : {
    "alert" : {
      "title" : "iOS遠(yuǎn)程消息钮科,我是主標(biāo)題唤衫!-title",
      "subtitle" : "iOS遠(yuǎn)程消息,我是主標(biāo)題绵脯!-Subtitle",
      "body" : "Dely,why am i so handsome -body"
    },
    "category" : "Dely_locationCategory",
    "badge" : "2"
  }
}

五佳励、Reference

iOS開(kāi)發(fā),本地推送的使用

iOS 10 消息推送(UserNotifications)秘籍總結(jié)(一)

iOS 10 消息推送(UserNotifications)秘籍總結(jié)(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蛆挫,隨后出現(xiàn)的幾起案子赃承,更是在濱河造成了極大的恐慌,老刑警劉巖悴侵,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧剖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡可免,警方通過(guò)查閱死者的電腦和手機(jī)抓于,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浇借,“玉大人捉撮,你說(shuō)我怎么就攤上這事〈伲” “怎么了呕缭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵堵泽,是天一觀的道長(zhǎng)修己。 經(jīng)常有香客問(wèn)我恢总,道長(zhǎng),這世上最難降的妖魔是什么睬愤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任片仿,我火速辦了婚禮,結(jié)果婚禮上尤辱,老公的妹妹穿的比我還像新娘砂豌。我一直安慰自己,他們只是感情好光督,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布阳距。 她就那樣靜靜地躺著,像睡著了一般结借。 火紅的嫁衣襯著肌膚如雪筐摘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天船老,我揣著相機(jī)與錄音咖熟,去河邊找鬼。 笑死柳畔,一個(gè)胖子當(dāng)著我的面吹牛馍管,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薪韩,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼确沸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了俘陷?” 一聲冷哼從身側(cè)響起张惹,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岭洲,沒(méi)想到半個(gè)月后宛逗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盾剩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年雷激,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片告私。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屎暇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驻粟,到底是詐尸還是另有隱情根悼,我是刑警寧澤凶异,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站挤巡,受9級(jí)特大地震影響剩彬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矿卑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一喉恋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧母廷,春花似錦轻黑、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至业舍,卻和暖如春抖拦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勤讽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工蟋座, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脚牍。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓向臀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诸狭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子券膀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351