背景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