IOS 推送通知 本地推送和遠(yuǎn)程推送

什么是推送通知?

首先明確:**此處的推送通知跟我們的”NSNotification”沒有半毛錢關(guān)系
可以理解為: 向用戶推送一條信息來通知用戶某件事情
作用: 可以在APP退到后臺(tái),或者關(guān)閉時(shí);繼續(xù)推送一條消息告訴用戶某件事情

推送通知的應(yīng)用場(chǎng)景?

  • (1) 一些任務(wù)管理APP,會(huì)在任務(wù)時(shí)間即將到達(dá)時(shí),通知你做該任務(wù);
    (2) 健身App定時(shí)提醒你應(yīng)該健身了;
    (3) 買過電影票后,提前半小時(shí)告訴你,電影即將開場(chǎng);
    (4) 當(dāng)你QQ或者微信收到消息時(shí),即使退到后臺(tái),或者關(guān)閉APP,也可以收到信息通知告訴我們;
    (5) 電商APP,推送一條消息通知我們有新品上架等等

推送通知的分類

  • 本地推送通知

    “本地”可以理解為”不聯(lián)網(wǎng)”;即使沒有網(wǎng)絡(luò)情況下,也可以推送通知消息
    應(yīng)用場(chǎng)景: 確定知道未來某個(gè)時(shí)間點(diǎn)應(yīng)該提醒用戶什么
    通知發(fā)送方:開發(fā)人員負(fù)責(zé)在app內(nèi)部發(fā)送

  • 遠(yuǎn)程推送通知

    與“本地”相對(duì),表示,必須在聯(lián)網(wǎng)情況下才會(huì)向用戶推送通知消息
    遠(yuǎn)程推送服務(wù)腮考,又稱為APNs(Apple Push Notification Services)

    應(yīng)用場(chǎng)景:

    1. 不確定未來某個(gè)時(shí)間點(diǎn)應(yīng)該提醒用戶什么,臨時(shí)性的
    2. 當(dāng)APP徹底退出時(shí)也想繼續(xù)讓用戶獲取一些最新消息
  • 使用原則: 誰能確定通知時(shí)間和內(nèi)容, 誰就可以發(fā)送(開發(fā)人員在APP內(nèi)部通過代碼發(fā)送=本地通知; 服務(wù)器可以確定通知時(shí)間和內(nèi)容=遠(yuǎn)程通知)*

遠(yuǎn)程推送的呈現(xiàn)效果

image.png

本地推送通知

在iOS8.0之后 使用本地通知需要得到用戶的許可 在didFinishLaunchingWithOptions里面添加請(qǐng)求

if(系統(tǒng)版本 >= 8.0)
{
    // 注冊(cè)接收通知的類型
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
    [application registerUserNotificationSettings:settings];

    // 注冊(cè)允許接收遠(yuǎn)程推送通知
    [application registerForRemoteNotifications];
}
else
{
    // 如果是iOS7.0堡赔,使用以下方法注冊(cè)
    [application registerForRemoteNotificationTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound];
}

//swfit:

 if #available(iOS 8.0, *)
        {
            // 注冊(cè)接收通知的類型
            let type = UIUserNotificationType.alert.rawValue | UIUserNotificationType.badge.rawValue | UIUserNotificationType.sound.rawValue
            let settings = UIUserNotificationSettings(types: UIUserNotificationType(rawValue: type), categories: nil)
            
            UIApplication.shared.registerUserNotificationSettings(settings)
            // 注冊(cè)允許接收遠(yuǎn)程推送通知
            UIApplication.shared.registerForRemoteNotifications()
        }
          

發(fā)送一個(gè)本地通知

@IBAction func puchNotionfication(_ sender: Any)
    {
    // 如果是ios8.0以前, 以下代碼, 可以直接發(fā)送一個(gè)本地通知,
    // 但是, 如果是ios8.0以后, 你需要主動(dòng)的請(qǐng)求授權(quán), 才可以
    // 通知顯示的條件
    // 必須不能再前臺(tái)
        
        //1.創(chuàng)建一個(gè)t本地通知
        let localNoti = UILocalNotification()
        //2. 設(shè)置通知內(nèi)容
        localNoti.alertBody = "這是一個(gè)好日字"
        //3. 設(shè)置發(fā)送通知的時(shí)間觸發(fā)時(shí)間
        localNoti.fireDate = Date(timeIntervalSinceNow: 4) 
    
        //重復(fù)周期
        // localNoti.repeatInterval = .weekday
        //設(shè)置滑動(dòng)文字
        localNoti.hasAction = true
        localNoti.alertAction = "回復(fù)"
        // 啟動(dòng)圖片(當(dāng)用戶點(diǎn)擊了本地通知, 啟動(dòng)我們APP 的時(shí)候, 帶的啟動(dòng)圖片)
               // 如果是在ios9.0以前, 當(dāng)鎖屏界面, 出現(xiàn)一個(gè)通知, 用戶點(diǎn)擊了通知, 啟動(dòng)APP 的時(shí)候, 會(huì)自動(dòng)將我們?cè)O(shè)置的圖片, 當(dāng)做啟動(dòng)圖像 來顯示
               // ios9.0, 這個(gè)屬性, 不太靈
               // 如果這個(gè)圖片,找不到, 會(huì)使用系統(tǒng)默認(rèn)的啟動(dòng)圖片
        localNoti.alertLaunchImage = "2"
        
        
               // 設(shè)置通知彈框的標(biāo)題
               // 標(biāo)題, 這對(duì)于, 通知中心的通知有效
        if #available(iOS 8.2, *)
        {
            localNoti.alertTitle = "斗地主"
        }

        // 設(shè)置圖標(biāo)右上角的數(shù)字(0 代表不顯示)
        localNoti.applicationIconBadgeNumber = 3
         // userInfo 額外信息
        localNoti.userInfo = ["name":"哥哥", "sex":"女"]
        

    
        //應(yīng)用程序級(jí)別的操作 ,調(diào)度本地通知,完成之后琐驴,會(huì)在特定的fireDate發(fā)出通知

        UIApplication.shared.scheduleLocalNotification(localNoti)
        
    }

其他的屬性和方法

調(diào)度本地推送通知(調(diào)度完畢后岖常,推送通知會(huì)在特地時(shí)間fireDate發(fā)出)
[[UIApplication sharedApplication] scheduleLocalNotification:ln];

獲得被調(diào)度(定制)的所有本地推送通知
@property(nonatomic,copy) NSArray *scheduledLocalNotifications;
(已經(jīng)發(fā)出且過期的推送通知就算調(diào)度結(jié)束胁勺,會(huì)自動(dòng)從這個(gè)數(shù)組中移除)

取消調(diào)度本地推送通知
- (void)cancelLocalNotification:(UILocalNotification *)notification;
- (void)cancelAllLocalNotifications;

立即發(fā)出本地推送通知
- (void)presentLocalNotificationNow:(UILocalNotification *)notification;


每隔多久重復(fù)發(fā)一次推送通知
@property(nonatomic) NSCalendarUnit repeatInterval;

點(diǎn)擊推送通知打開app時(shí)顯示的啟動(dòng)圖片
@property(nonatomic,copy) NSString *alertLaunchImage;

附加的額外信息
@property(nonatomic,copy) NSDictionary *userInfo;

時(shí)區(qū)
@property(nonatomic,copy) NSTimeZone *timeZone;
(一般設(shè)置為[NSTimeZone defaultTimeZone] ,跟隨手機(jī)的時(shí)區(qū))

點(diǎn)擊本地通知

當(dāng)用戶點(diǎn)擊本地推送通知暑诸,會(huì)自動(dòng)打開app逗威,這里有2種情況

  1. app并沒有關(guān)閉峰搪,一直隱藏在后臺(tái)
    讓app進(jìn)入前臺(tái),并會(huì)調(diào)用AppDelegate的下面方法(并非重新啟動(dòng)app)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
  1. app已經(jīng)被關(guān)閉(進(jìn)程已死)
    啟動(dòng)app凯旭,啟動(dòng)完畢會(huì)調(diào)用AppDelegate的下面方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
launchOptions參數(shù)通過UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知對(duì)象

添加一些額外的操作

IMG_0497.PNG
func registerAuthor()
    {
        // Override point for customization after application launch.
           if #available(iOS 8.0, *)
           {
               // 注冊(cè)接收通知的類型
               let type = UIUserNotificationType.alert.rawValue | UIUserNotificationType.badge.rawValue | UIUserNotificationType.sound.rawValue
            
               
            
            
            //1.創(chuàng)建z一組操作行為
            let category = UIMutableUserNotificationCategory()
            //1.1設(shè)置組標(biāo)識(shí)
            category.identifier = "select"
            //1.2設(shè)置組里面的行為
            let action1 = UIMutableUserNotificationAction()
            action1.identifier = "queding"
            action1.title = "確定"
            // behavior todo
                       
            // 代表, 用戶如果點(diǎn)擊了這個(gè)動(dòng)作, 拉到底, 是在前臺(tái)運(yùn)行這個(gè)動(dòng)作, 還是在后臺(tái)
            action1.activationMode = .foreground
             // 必須要解鎖之后, 行為才會(huì)執(zhí)行(如果activationMode, 是前臺(tái)狀態(tài), 那這個(gè)屬性, 就會(huì)被忽略)
            action1.isAuthenticationRequired = true
            // 是否是破壞性行為(會(huì)使用一個(gè)紅色的標(biāo)識(shí), 來標(biāo)識(shí)這個(gè)按鈕)
            action1.isDestructive = false
            
            //1.1設(shè)置組標(biāo)識(shí)
            category.identifier = "select"
            
            //1.2設(shè)置組里面的行為
            let action2 = UIMutableUserNotificationAction()
            action2.identifier = "huifu"
            action2.title = "回復(fù)"
            
            // behavior todo
            if #available(iOS 9.0, *)
            {
                action2.behavior = .textInput
                action2.parameters = [UIUserNotificationTextInputActionButtonTitleKey: "確定"]
                    
            }
                                      
            // 代表, 用戶如果點(diǎn)擊了這個(gè)動(dòng)作, 拉到底, 是在前臺(tái)運(yùn)行這個(gè)動(dòng)作, 還是在后臺(tái)
            action2.activationMode = .background
            // 必須要解鎖之后, 行為才會(huì)執(zhí)行(如果activationMode, 是前臺(tái)狀態(tài), 那這個(gè)屬性, 就會(huì)被忽略)
            action2.isAuthenticationRequired = false
            // 是否是破壞性行為(會(huì)使用一個(gè)紅色的標(biāo)識(shí), 來標(biāo)識(shí)這個(gè)按鈕)
            action2.isDestructive = false
            
            
            let actions = [action1, action2]
            
            // 如果針對(duì)于彈框樣式的通知
            // default 代表, 最多可以顯示4個(gè)按鈕
            // minimal, 代表,最多可以顯示2個(gè)按鈕
            category.setActions(actions, for: .default)
            
            //附加操作行為組
            let categorys : Set<UIUserNotificationCategory> = [category]
            
               let settings = UIUserNotificationSettings(types: UIUserNotificationType(rawValue: type), categories: categorys)
                UIApplication.shared.registerUserNotificationSettings(settings)
            
               // 注冊(cè)允許接收遠(yuǎn)程推送通知
               UIApplication.shared.registerForRemoteNotifications()
           }
               
        
    }
     // completionHandler, 系統(tǒng)提供的回調(diào)代碼塊, 執(zhí)行這個(gè)代碼塊, 到時(shí)候, 系統(tǒng)會(huì)采集一些信息
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void)
    {
        print(identifier,notification)
        print("old")
        completionHandler()
    }
    // 9.0
    // 如果實(shí)現(xiàn)了這個(gè)方法, 那么上面一個(gè)方法, 就不再執(zhí)行
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        print("new")
        print(identifier,responseInfo)
        completionHandler()
    }

遠(yuǎn)程推送

什么是遠(yuǎn)程推送通知

  • 顧名思義概耻,就是從遠(yuǎn)程服務(wù)器推送給客戶端的通知(需要聯(lián)網(wǎng))
  • 遠(yuǎn)程推送服務(wù),又稱為APNs(Apple Push Notification Services

為什么需要遠(yuǎn)程推送通知尽纽?

傳統(tǒng)獲取數(shù)據(jù)的局限性
只要用戶關(guān)閉了app启盛,就無法跟app的服務(wù)器溝通擂送,無法從服務(wù)器上獲得最新的數(shù)據(jù)內(nèi)容
不管用戶打開還是關(guān)閉app,只要聯(lián)網(wǎng)了湿诊,都能接收到服務(wù)器推送的遠(yuǎn)程通知

所有的蘋果設(shè)備矫膨,在聯(lián)網(wǎng)狀態(tài)下差凹,都會(huì)與蘋果的服務(wù)器建立長(zhǎng)連接

  • 什么是長(zhǎng)連接
    只要聯(lián)網(wǎng)了,就一直建立連接

  • 長(zhǎng)連接的作用
    時(shí)間校準(zhǔn)
    系統(tǒng)升級(jí)
    查找我的iPhone
    .. ...

  • 長(zhǎng)連接的好處
    數(shù)據(jù)傳輸速度快
    數(shù)據(jù)保持最新狀態(tài)

獲取deviceToken

蘋果需要用戶的UDID和bundleID


獲取過程

qq案例分析

  1. app 發(fā)送設(shè)備的UDID和
    應(yīng)用的Bundle Identifier
    給APNs服務(wù)器

  2. 經(jīng)蘋果加密生成一個(gè)
    deviceToken 返還給當(dāng)前app

  3. app發(fā)送當(dāng)前用戶的deviceToken
    和用戶的標(biāo)志(比如id或者qq) 給qq服務(wù)器

  4. qq服務(wù)器將用戶的deviceToken存進(jìn)數(shù)據(jù)庫

  5. 當(dāng)有人發(fā)送消息 李四的手機(jī)(昵稱:李四 QQ:56789)
    發(fā)給張三(QQ12345):吃飯沒?

  6. 消息先到qq服務(wù)器侧馅,然后去數(shù)據(jù)庫查詢張三的deviceToken

  7. qq服務(wù)器通知蘋果服務(wù)器
    deviceTokoen:888
    body:李四:吃飯沒?

  8. 蘋果服務(wù)器通過deviceToken找到張三現(xiàn)在的設(shè)備

一.開發(fā)iOS程序的推送功能, iOS端需要做的事

1.請(qǐng)求蘋果獲得deviceToken
2.得到蘋果返回的deviceToken,發(fā)送deviceToken給公司的服務(wù)器

  1. 監(jiān)聽用戶對(duì)通知的點(diǎn)擊

二.調(diào)試iOS的遠(yuǎn)程推送功能, 必備條件:

1.真機(jī)

2.調(diào)試推送需要的證書文件
1> aps_development.cer : 某臺(tái)電腦就能調(diào)試某個(gè)app的推送服務(wù)
2> iphone5_qq.mobileprovision : 某臺(tái)電腦就能利用某臺(tái)設(shè)備調(diào)試某個(gè)程序

三.發(fā)布具有推送服務(wù)的app

1> aps_production.cer : 如果發(fā)布的程序中包含了推送服務(wù),就必須安裝這個(gè)證書
2> qq.mobileprovision : 某臺(tái)電腦就能發(fā)布某個(gè)程序

制作證書

截屏2020-05-27下午4.42.59.png
截屏2020-05-27下午4.43.18.png
截屏2020-05-27下午4.43.09.png
截屏2020-05-27下午4.43.25.png

截屏2020-05-27下午4.43.35.png
截屏2020-05-27下午4.43.44.png

注冊(cè)遠(yuǎn)程推送通知

客戶端如果想接收APNs的遠(yuǎn)程推送通知危尿,必須先注冊(cè)(得到用戶的授權(quán))
一般在App啟動(dòng)完畢后就馬上注冊(cè)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // 注冊(cè)遠(yuǎn)程通知
       UIRemoteNotificationType type = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound;
    [application registerForRemoteNotificationTypes:type];
    return YES;
}
  • 注冊(cè)成功后會(huì)調(diào)用AppDelegate的下面方法,得到設(shè)備的deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"%@", deviceToken);
}

點(diǎn)擊遠(yuǎn)程推送通知

當(dāng)用戶點(diǎn)擊遠(yuǎn)程推送通知馁痴,會(huì)自動(dòng)打開app谊娇,這里有2種情況
app并沒有關(guān)閉,一直隱藏在后臺(tái)
讓app進(jìn)入前臺(tái)罗晕,并會(huì)調(diào)用AppDelegate的下面方法(并非重新啟動(dòng)app)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;

app已經(jīng)被關(guān)閉(進(jìn)程已死)
啟動(dòng)app济欢,啟動(dòng)完畢會(huì)調(diào)用AppDelegate的下面方法

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
launchOptions參數(shù)通過UIApplicationLaunchOptionsRemoteNotificationKey取出服務(wù)器返回的字典內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市小渊,隨后出現(xiàn)的幾起案子法褥,更是在濱河造成了極大的恐慌,老刑警劉巖酬屉,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件半等,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呐萨,警方通過查閱死者的電腦和手機(jī)杀饵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谬擦,“玉大人凹髓,你說我怎么就攤上這事∏犹耄” “怎么了蔚舀?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵饵沧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我赌躺,道長(zhǎng)狼牺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任礼患,我火速辦了婚禮是钥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缅叠。我一直安慰自己悄泥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布肤粱。 她就那樣靜靜地躺著弹囚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪领曼。 梳的紋絲不亂的頭發(fā)上鸥鹉,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音庶骄,去河邊找鬼毁渗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛单刁,可吹牛的內(nèi)容都是我干的灸异。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼羔飞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绎狭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起褥傍,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤儡嘶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恍风,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹦狂,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年朋贬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凯楔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锦募,死狀恐怖摆屯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤虐骑,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布准验,位于F島的核電站,受9級(jí)特大地震影響廷没,放射性物質(zhì)發(fā)生泄漏糊饱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一颠黎、第九天 我趴在偏房一處隱蔽的房頂上張望另锋。 院中可真熱鬧,春花似錦狭归、人聲如沸夭坪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽室梅。三九已至,卻和暖如春潭流,著一層夾襖步出監(jiān)牢的瞬間竞惋,已是汗流浹背柜去。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工灰嫉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗓奢。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓讼撒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親股耽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子根盒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355