iOS 10 消息推送(UserNotifications)祕籍總結(一)

背景html

 

iOS10 新特性一出,各個大神就早已研究新特性能給場景智能化所帶來的好處(唉,惋惜我只是一個小白)。我也被安排適配iOS10的推送工做!ios

 

Apple 表示這是 iOS 有史以來最大的升級(our biggest release yet),更加智能開放的 Siri 、強化應用對 3D Touch 支持、 HomeKit 、電話攔截及全新設計的通知等等…git

 

iOS 10 中將以前繁雜的推送通知統一成UserNotifications.framework 來集中管理和使用通知功能,還增長一些實用的功能——撤回單條通知、更新已展現通知、中途修改通知內容、在通知中顯示多媒體資源、自定義UI等功能,功能着實強大!github

 

本文主要是針對iOS 10的消息通知作介紹,因此不少代碼沒有對iOS 10以前作添加適配。服務器

 

基本原理網絡

 

iOS推送分爲Local Notifications(本地推送) 和 Remote Notifications(遠程推送)(原理圖來源於網絡,若有侵權請告知,我會添加來源,我怕我賠不起)app

 

Local Notifications(本地推送)ide

    • App本地建立通知,加入到系統的Schedule裏,工具

    • 若是觸發器條件達成時會推送相應的消息內容性能

Remote Notifications(遠程推送)

 

圖中,Provider是指某個iPhone軟件的Push服務器,這篇文章我將使用我花了12塊大洋(心疼)買的 APNS Pusher 做爲個人推送源。

 

APNS 是Apple Push Notification Service(Apple Push服務器)的縮寫,是蘋果的服務器。

 

上圖能夠分爲三個階段:

 

第一階段:APNS Pusher應用程序把要發送的消息、目的iPhone的標識打包,發給APNS。

 

第二階段:APNS在自身的已註冊Push服務的iPhone列表中,查找有相應標識的iPhone,並把消息發到iPhone。

 

第三階段:iPhone把發來的消息傳遞給相應的應用程序, 而且按照設定彈出Push通知。

 

從上圖咱們能夠看到:

 

  1. 首先是應用程序註冊消息推送。

  2. IOS跟APNS Server要deviceToken。應用程序接受deviceToken。

  3. 應用程序將deviceToken發送給PUSH服務端程序。

  4. 服務端程序向APNS服務發送消息。

  5. APNS服務將消息發送給iPhone應用程序。

 

基本配置和基本方法

 

若是隻是簡單的本地推送,跳過1 2 步驟,直接到3

 

一、 若是你的App有遠端推送的話,那你須要開發者帳號的,須要新建一個對應你bundle的push 證書。證書這一塊我就不說了,若是針對證書有什麼問題能夠給我留言,我會單獨把證書相關的知識點整理起來!固然本人是很是喜歡的分享的(又裝逼),若是你沒有帳號,我能夠把我測試用的證書發給你,用於你的測試和學習,私聊我。

 

二、 Capabilities中打開Push Notifications 開關

在XCode7中這裏的開關不打開,推送也是能夠正常使用的,可是在XCode8中,這裏的開關必需要打開,否則會報錯:

 

Error Domain=NSCocoaErrorDomain Code=3000 "未找到應用程序的「aps-environment」的受權字符串" UserInfo={NSLocalizedDescription=未找到應用程序的「aps-environment」的受權字符串}

 

打開後會自動在項目裏生成entitlements文件。

 

Push Notification開關.png

entitlements文件.png

三、 推送的註冊

 

第一步: 導入 #import 

且要遵照的協議,在Appdelegate.m中。

這裏須要注意,咱們最好寫成這種形式(防止低版本找不到頭文件出現問題)

 

#ifdef NSFoundationVersionNumber_iOS_9_x_Max

#import

#endif

 

第二步:咱們須要在

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中註冊通知,代碼以下

 

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [self replyPushNotificationAuthorization:application];

    return YES;

}

 

 

#pragma mark - 申請通知權限

// 申請通知權限

- (void)replyPushNotificationAuthorization:(UIApplication *)application{

 

    if (IOS10_OR_LATER) {

        //iOS 10 later

        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

        //必須寫代理,否則沒法監聽通知的接收與點擊事件

        center.delegate = self;

        [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {

            if (!error && granted) {

                //用戶點擊容許

                NSLog(@"註冊成功");

            }else{

                //用戶點擊不容許

                NSLog(@"註冊失敗");

            }

        }];

 

        // 能夠經過 getNotificationSettingsWithCompletionHandler 獲取權限設置

        //以前註冊推送服務,用戶點擊了贊成仍是不一樣意,以及用戶以後又作了怎樣的更改咱們都無從得知,如今 apple 開放了這個 API,咱們能夠直接獲取到用戶的設定信息了。注意UNNotificationSettings是隻讀對象哦,不能直接修改!

        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

            NSLog(@"========%@",settings);

        }];

    }else if (IOS8_OR_LATER){

        //iOS 8 - iOS 10系統

        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];

        [application registerUserNotificationSettings:settings];

    }else{

        //iOS 8.0系統如下

        [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];

    }

 

    //註冊遠端消息通知獲取device token

    [application registerForRemoteNotifications];

}

 

上面須要注意:

 

1. 必須寫代理,否則沒法監聽通知的接收與點擊事件

center.delegate = self;

 

下面是我在項目裏定義的宏

#define IOS10_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0)

#define IOS9_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)

#define IOS8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

#define IOS7_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

 

2. 以前註冊推送服務,用戶點擊了贊成仍是不一樣意,以及用戶以後又作了怎樣的更改咱們都無從得知,如今 apple 開放了這個 API,咱們能夠直接獲取到用戶的設定信息了。注意UNNotificationSettings是隻讀對象哦,不能直接修改!只能經過如下方式獲取

[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

            NSLog(@"========%@",settings);

  }];

打印信息以下:

========

 

四、 遠端推送須要獲取設備的Device Token的方法是沒有變的,代碼以下

 

#pragma  mark - 獲取device Token

//獲取DeviceToken成功

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{

 

    //解析NSData獲取字符串

    //我看網上這部分直接使用下面方法轉換爲string,你會獲得一個nil(別怪我不告訴你哦)

    //錯誤寫法

    //NSString *string = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];

 

 

    //正確寫法

    NSString *deviceString = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@""]];

    deviceString = [deviceString stringByReplacingOccurrencesOfString:@" " withString:@""];

 

    NSLog(@"deviceToken===========%@",deviceString);

}

 

//獲取DeviceToken失敗

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{

    NSLog(@"[DeviceToken Error]:%@\n",error.description);

}

 

五、這一步吊了,這是iOS 10系統更新時,蘋果給了咱們2個代理方法來處理通知的接收和點擊事件,這兩個方法在的協議中,你們能夠查看下。

 

@protocol UNUserNotificationCenterDelegate

 

@optional

 

// The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

 

// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:.

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

 

@end

 

此外,蘋果把本地通知跟遠程通知合二爲一。區分本地通知跟遠程通知的類是UNPushNotificationTrigger.h類中,UNPushNotificationTrigger的類型是新增長的,經過它,咱們能夠獲得一些通知的觸發條件 ,解釋以下:

 

  1. UNPushNotificationTrigger (遠程通知) 遠程推送的通知類型

  2. UNTimeIntervalNotificationTrigger (本地通知) 必定時間以後,重複或者不重複推送通知。咱們能夠設置timeInterval(時間間隔)和repeats(是否重複)。

  3. UNCalendarNotificationTrigger(本地通知) 必定日期以後,重複或者不重複推送通知 例如,你天天8點推送一個通知,只要dateComponents爲8,若是你想天天8點都推送這個通知,只要repeats爲YES就能夠了。

  4. UNLocationNotificationTrigger (本地通知)地理位置的一種通知,

    當用戶進入或離開一個地理區域來通知。

    如今先提出來,後面我會一一代碼演示出每種用法。仍是回到兩個很吊的代理方法吧

 

#pragma mark - iOS10 收到通知(本地和遠端) UNUserNotificationCenterDelegate

 

//App處於前臺接收通知時

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{

 

    //收到推送的請求

    UNNotificationRequest *request = notification.request;

 

    //收到推送的內容

    UNNotificationContent *content = request.content;

 

    //收到用戶的基本信息

    NSDictionary *userInfo = content.userInfo;

 

    //收到推送消息的角標

    NSNumber *badge = content.badge;

 

    //收到推送消息body

    NSString *body = content.body;

 

    //推送消息的聲音

    UNNotificationSound *sound = content.sound;

 

    // 推送消息的副標題

    NSString *subtitle = content.subtitle;

 

    // 推送消息的標題

    NSString *title = content.title;

 

    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {

        //此處省略一萬行需求代碼。。。。。。

        NSLog(@"iOS10 收到遠程通知:%@",userInfo);

 

    }else {

        // 判斷爲本地通知

        //此處省略一萬行需求代碼。。。。。。

        NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);

    }

 

 

    // 須要執行這個方法,選擇是否提醒用戶,有Badge、Sound、Alert三種類型能夠設置

    completionHandler(UNNotificationPresentationOptionBadge|

                      UNNotificationPresentationOptionSound|

                      UNNotificationPresentationOptionAlert);

 

}

 

 

//App通知的點擊事件

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{

    //收到推送的請求

    UNNotificationRequest *request = response.notification.request;

 

    //收到推送的內容

    UNNotificationContent *content = request.content;

 

    //收到用戶的基本信息

    NSDictionary *userInfo = content.userInfo;

 

    //收到推送消息的角標

    NSNumber *badge = content.badge;

 

    //收到推送消息body

    NSString *body = content.body;

 

    //推送消息的聲音

    UNNotificationSound *sound = content.sound;

 

    // 推送消息的副標題

    NSString *subtitle = content.subtitle;

 

    // 推送消息的標題

    NSString *title = content.title;

 

    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {

        NSLog(@"iOS10 收到遠程通知:%@",userInfo);

        //此處省略一萬行需求代碼。。。。。。

 

    }else {

        // 判斷爲本地通知

        //此處省略一萬行需求代碼。。。。。。

        NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);

    }

 

    //2016-09-27 14:42:16.353978 UserNotificationsDemo[1765:800117] Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.

    completionHandler(); // 系統要求執行這個方法

}

 

須要注意的:

 

1.下面這個代理方法,只會是app處於前臺狀態 前臺狀態 and 前臺狀態下才會走,後臺模式下是不會走這裏的

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler

 

 

2.下面這個代理方法,只會是用戶點擊消息纔會觸發,若是使用戶長按(3DTouch)、Action等並不會觸發。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler

 

3.點擊代理最後須要執行:completionHandler(); // 系統要求執行這個方法

否則會報:

2016-09-27 14:42:16.353978 UserNotificationsDemo[1765:800117] Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.

 

4.無論前臺後臺狀態下。推送消息的橫幅均可以展現出來!後臺狀態不用說,前臺時須要在前臺代理方法中設置 ,設置以下:

// 須要執行這個方法,選擇是否提醒用戶,有Badge、Sound、Alert三種類型能夠設置

completionHandler(UNNotificationPresentationOptionBadge|

                  UNNotificationPresentationOptionSound|

                  UNNotificationPresentationOptionAlert);

 

六、 iOS 10以前接收通知的兼容方法

 

#pragma mark -iOS 10以前收到通知

 

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

    NSLog(@"iOS6及如下系統,收到通知:%@", userInfo);

    //此處省略一萬行需求代碼。。。。。。

}

 

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    NSLog(@"iOS7及以上系統,收到通知:%@", userInfo);

    completionHandler(UIBackgroundFetchResultNewData);

    //此處省略一萬行需求代碼。。。。。。

}

 

段結:是否是覺得就結束了?NO NO NO(你覺得離開了幻境,其實才剛剛踏入幻境!)上面的介紹了基本原理、基本配置以及基本方法說明,如今作完這些工做,咱們的學習纔剛剛開始!如今天時、地利、人和、能夠開始下面推送coding的學習和測試了。

 

在用戶平常生活中會有不少種情形須要通知,好比:新聞提醒、定時吃藥、按期體檢、到達某個地方提醒用戶等等,這些功能在 UserNotifications 中都提供了相應的接口。

 

咱們先學會基本的技能簡單的推送(爬),後面在學習進階定製推送(走),最後看看能不能高級推送(飛不飛起來看我的了,我是飛不起來):

 

基本Local Notifications(本地推送) 和 Remote Notifications(遠程推送)

 

1、 基本的本地推送

 

本地推送生成主要流程就是:

 

1. 建立一個觸發器(trigger)

2. 建立推送的內容(UNMutableNotificationContent)

3. 建立推送請求(UNNotificationRequest)

4. 推送請求添加到推送管理中心(UNUserNotificationCenter)中

 

一、新功能trigger能夠在特定條件觸發,有三類:UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger、UNLocationNotificationTrigger

 

1.一、 UNTimeIntervalNotificationTrigger:一段時間後觸發(定時推送)

 

//timeInterval:單位爲秒(s)  repeats:是否循環提醒

//50s後提醒

UNTimeIntervalNotificationTrigger *trigger1 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:50 repeats:NO];

 

1.2 UNCalendarNotificationTrigger :調用

+ (instancetype)triggerWithDateMatchingComponents:(NSDateComponents *)dateComponents repeats:(BOOL)repeats;進行註冊;時間點信息用 NSDateComponents.(按期推送)

 

//在每週一的14點3分提醒

NSDateComponents *components = [[NSDateComponents alloc] init];

components.weekday = 2;

components.hour = 16;

components.minute = 3;

// components 日期

UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];

 

1.三、UNLocationNotificationTrigger:調用

+ (instancetype)triggerWithRegion:(CLRegion *)region repeats:(BOOL)repeats;

進行註冊,地區信息使用CLRegion的子類CLCircularRegion,能夠配置region屬性 notifyOnEntry和notifyOnExit,是在進入地區、從地區出來或者二者都要的時候進行通知,這個測試過程專門從公司跑到家時刻關注手機有推送嘛,果真是有的(定點推送)

 

  //首先得導入#import ,否則會regin建立有問題。

  // 建立位置信息

  CLLocationCoordinate2D center1 = CLLocationCoordinate2DMake(39.788857, 116.5559392);

  CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center1 radius:500 identifier:@"經海五路"];

  region.notifyOnEntry = YES;

  region.notifyOnExit = YES;

  // region 位置信息 repeats 是否重複 (CLRegion 能夠是地理位置信息)

  UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];

 

二、建立推送的內容(UNMutableNotificationContent)

UNNotificationContent:屬性readOnly

UNMutableNotificationContent:屬性有title、subtitle、body、badge、sound、lauchImageName、userInfo、attachments、categoryIdentifier、threadIdentifier

 

本地消息內容 內容限制大小 展現
title NSString 限制在一行,多出部分省略號
subtitle NSString 限制在一行,多出部分省略號
body NSString 通知欄出現時,限制在兩行,多出部分省略號;預覽時,所有展現

 

注意點: body中printf風格的轉義字符,好比說要包含%,須要寫成%% 纔會顯示,\一樣

 

三、建立完整的本地推送請求Demo

運行結果以下:

2、 基本的遠端推送

 

若是你想模擬遠端推送,按照我前面介紹的配置基本環境、證書、push開關和基本方法就能夠模擬遠端的基本遠端推送。

一、運行工程則會拿到設備的Device Token,後面會用到。

 

二、如今咱們須要一個推送服務器給APNS發送信息。我前面說了我花了12塊大洋(心疼死我了)買了一個APNS pusher 來模擬遠端推送服務,固然你能夠不花錢也能夠用到,例如:

NWPusher

三、你須要把你剛剛獲取的device token填到相應位置,同時你要配置好push證書哦。

 

四、須要添加aps內容了,而後點擊send就OK了

 

{

  "aps" : {

    "alert" : {

      "title" : "iOS遠程消息,我是主標題!-title",

      "subtitle" : "iOS遠程消息,我是主標題!-Subtitle",

      "body" : "Dely,why am i so handsome -body"

    },

    "badge" : "2"

  }

}

 

五、稍縱即逝你就收到了遠端消息了

 

 

六、Notification Management

對推送進行查、改、刪。都須要一個必需的參數requestIdentifier

 

一、更新通知

 

Local Notification須要經過更新request.相同的requestIdentifier,從新添加到推送center就能夠了,說白了就是從新建立local Notification request(只要保證requestIdentifier就ok了),應用場景如圖

 

Local Notification更新前.png

Local Notification更新後.png

Remote Notification 更新須要經過新的字段apps-collapse-id來做爲惟一標示,我前面用的APNS pusher暫不支持這個字段,不過github上有不少這樣的工具:

https://github.com/KnuffApp/Knuff

這樣remote 也能夠更新推送消息

 

二、推送消息的查找和刪除

 

測試以下:

段結: 收到通知時你須要在appdelegate裏面的代理方法裏處理你的需求邏輯,這個須要你本身寫了。到目前爲止你掌握了基本的本地推送和基本的遠端推送!

 

不知不覺寫了這麼多字(全是TM廢話)、原本繼續打算寫進階的本地和遠端推送(Media Attachments、Notification Actions、自定義推送界面等),留着下一篇博客繼續分享吧,欲知後事如何,且聽下會裝X!

 

若是你喜歡能夠點個喜歡^_^(竟有如此厚顏無恥之人)

 

下集預告:

 

參考資料:

 

https://developer.apple.com/reference/usernotifications

http://www.jianshu.com/p/b74e52e866fc

http://www.jianshu.com/p/b74e52e866fc

http://blog.csdn.net/he317165264/article/details/52574934

http://qoofan.com/read/PnEaMEZonD.html

http://www.qingpingshan.com/rjbc/ios/140921.html

相關文章
相關標籤/搜索