背景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通知。
從上圖咱們能夠看到:
首先是應用程序註冊消息推送。
IOS跟APNS Server要deviceToken。應用程序接受deviceToken。
應用程序將deviceToken發送給PUSH服務端程序。
服務端程序向APNS服務發送消息。
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的類型是新增長的,經過它,咱們能夠獲得一些通知的觸發條件 ,解釋以下:
UNPushNotificationTrigger (遠程通知) 遠程推送的通知類型
UNTimeIntervalNotificationTrigger (本地通知) 必定時間以後,重複或者不重複推送通知。咱們能夠設置timeInterval(時間間隔)和repeats(是否重複)。
UNCalendarNotificationTrigger(本地通知) 必定日期以後,重複或者不重複推送通知 例如,你天天8點推送一個通知,只要dateComponents爲8,若是你想天天8點都推送這個通知,只要repeats爲YES就能夠了。
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