在今年 6月14号 苹果开发者大会 WWDC 2016 以后,笔者赶忙就去 apple 的开发者网站下载了最新的 Xcode 8 beta 和 iOS 10 beta,而后在本身的手机上装了 iOS 10 beta ,狠狠地体验了一把。
能够说 iOS 10 不管从界面风格,仍是 Framework 都作了不少改动。最直观的感觉就是界面的圆角增多了,系统动画更加多样和流畅,系统 App 的功能也变得更丰富了。ios
而 iOS 10 里的推送功能,也较以前更增强大,
今天咱们就来聊聊 iOS 10 里的推送功能。数组
首先咱们一块儿简单回顾下 iOS 10 之前的推送服务。
iOS 推送分为 Local Notifications(本地推送) 和 Remote Notifications(远程推送),先看 2 张图:服务器
简单的说就是本地推送经过 App 本地定制,加入到系统的 Schedule 里,而后在指定的时间推送指定文字。而远程推送经过服务端向苹果推送服务器 Apple Push Notification Service (APNs) 发送 Notification Payload,以后 APNs 再将推送下发到指定设备的 指定 App 上。
以及 iOS 7 以后在不显式地弹窗打扰用户的状况下,进行的静默推送:app
具体作法能够参考 iOS 7 Background Remote Notificationide
好,扯了这么多,该进入今天的正题了 —— User Notifications Framework 。
首先在 AppDelegate.m
中动画
#import <UserNotifications/UserNotifications.h>
如下分别是 iOS 10 以前和以后的注册方式,其中的 UNAuthorizationOptions
里还能够找到 1 个 UNAuthorizationOptionCarPlay
的值是专为车载系统定制的值。网站
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //iOS 10 before UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]; [application registerUserNotificationSettings:settings]; //iOS 10 UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) { if (!error) { NSLog(@"request authorization succeeded!"); } }]; return YES; }
以前注册推送服务,ios 8 及以前使用了不一样的 API,而且返回结果也不一样。如今 apple 不只统一了这个 API,并且咱们能够获取到用户更加详细的设定了。编码
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { NSLog(@"%@",settings); }];
打印得到以下信息:加密
<UNNotificationSettings: 0x16567310; authorizationStatus: Authorized, notificationCenterSetting: Enabled, soundSetting: Enabled, badgeSetting: Enabled, lockScreenSetting: Enabled, alertSetting: NotSupported, carPlaySetting: Enabled, alertStyle: Banner>
跟以前同样url
[[UIApplication sharedApplication] registerForRemoteNotifications];
之前只能展现一条文字,如今能够有 title 、subtitle 以及 body 了。
定制方法以下:
//Local Notification UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"Introduction to Notifications"; content.subtitle = @"Session 707"; content.body = @"Woah! These new notifications look amazing! Don’t you agree?"; content.badge = @1; //Remote Notification { "aps" : { "alert" : { "title" : "Introduction to Notifications", "subtitle" : "Session 707", "body" : "Woah! These new notifications look amazing! Don’t you agree?" }, "badge" : 1 }, }
又是一个新的功能,有三种
//2 分钟后提醒 UNTimeIntervalNotificationTrigger *trigger1 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:120 repeats:NO]; //每小时重复 1 次喊我喝水 UNTimeIntervalNotificationTrigger *trigger2 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:3600 repeats:YES]; //每周一早上 8:00 提醒我给老婆作早饭 NSDateComponents *components = [[NSDateComponents alloc] init]; components.weekday = 2; components.hour = 8; UNCalendarNotificationTrigger *trigger3 = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES]; //#import <CoreLocation/CoreLocation.h> //一到麦当劳就喊我下车 CLRegion *region = [[CLRegion alloc] init]; UNLocationNotificationTrigger *trigger4 = [UNLocationNotificationTrigger triggerWithRegion:region repeats:NO];
NSString *requestIdentifier = @"sampleRequest"; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:trigger1]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { }];
而后整个推送的过程就变成了酱紫:
Content
和 Trigger
向 UNUserNotificationCenter
进行 request
这三部曲来实现。APNs
发送 Notification Payload
。设定了推送,而后就结束了?iOS 10 并无这么简单!
经过实现协议,使 App 处于前台时捕捉并处理即将触发的推送:
@interface AppDelegate () <UNUserNotificationCenterDelegate> -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{ completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound); }
让它只显示 alert 和 sound ,而忽略 badge 。
完全掌控整个推送周期:
apns-collapse-id
经过以前的 addNotificationRequest:
方法,在 id
不变的状况下从新添加,就能够刷新原有的推送。
NSString *requestIdentifier = @"sampleRequest"; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:newContent trigger:newTrigger1]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { }];
删除计划的推送:
[center removePendingNotificationRequestsWithIdentifiers:@[requestIdentifier]];
此外 UNUserNotificationCenter.h
中还有诸如删除全部推送、查看已经发出的推送、删除已经发出的推送等等强大的接口。
刷新原有的推送后,在通知中心的显示里,也会有相应的变化,这里注意第 2 条信息,如今比分是 1:0
比分刷新后为 1:1,在不产生新的推送条目的状况下位置被前置了!
试想利用这个方法,不断的刷新推送,是否是就能够作到让本身 App 的推送内容始终展现在用户手机通知中心的最顶端,力压其他全部内容了呢?总感受有点不厚道啊~
关于推送的更多相似 Media Attachments
的高级功能,咱们将在下一篇里详细讨论。
为推送添加更多媒体附件,诸如图片、音乐
在 iOS 10 中,能够容许推送添加交互操做 action
,这些 action
可使得 App 在前台或后台执行一些逻辑代码。而且在锁屏界面经过 3d-touch 触发。如:推出键盘进行快捷回复,该功能以往只在 iMessage 中可行。
(Notification Actions 在 iOS 8 引入,快捷回复在 iOS 9 引入,在 iOS 10 中,这些 API 被统一。)
在 iOS 10 中,这叫 category
,是对推送功能的一个拓展,能够经过 3d-touch 触发。
建立 action
即一项交互操做
title
是交互按钮的内容
options
可让该 action
成为一条可在前台执行的 action
建立:
UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:@"reply" title:@"Reply" options:UNNotificationActionOptionNone];
建立 category
可添加多个 action
的数组,就像图片中同样,有多种操做
其中的 id
,须要填写你想要添加到哪一个推送消息的 id
建立:
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"message" actions:@[action] minimalActions:@[action] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
把 category
添加到通知中心:
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithArray:@[category]]];
触发方式:
Remote Notifications 配置 payload,指定其中 category 的值与第 2 步中 Identifier 一致:
{ aps : { alert : "Welcome to WWDC !", category : "message" } }
Local Notifications 只须要在建立 contnet 的时候指定 Id 便可:(content 相关内容请参照 上一篇 中的 Content 部分)
content。categoryIdentifier = @"message";
锁屏及在通知中心收到推送,侧滑,会展现 action。
只要点击 Clear 就能够将该条推送清除,而且重复的内容不会被发送到你的其余 iOS 设备上。
跟 Notification Actions 只有一点小区别,就是添加 action 到 category 的时候,增长一个 option 的值 UNNotificationCategoryOptionCustomDismissAction:
UNNotificationAction *clearAction = [UNNotificationAction actionWithIdentifier:@"clear" title:@"clear" options:UNNotificationActionOptionNone]; UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"clear" actions:@[clearAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];//这里增长一个 dismiss 的值
用户点击这些 actions 之后,是启动 App、触发键盘、清除通知或是有其余的响应,这些所有只须要实现协议 UNUserNotificationCenterDelegate 中的一个方法就能够控制:
@interface ClassName () <UNUserNotificationCenterDelegate> -(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{ }
其中的 response 包含如下内容:
其中的 trigger 能够用来判断是远程推送仍是本地推送。
处理 response 举例:
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{ NSString *categoryIdentifier = response.notification.request.content.categoryIdentifier; if ([categoryIdentifier isEqualToString:@"handle category"]) {//识别须要被处理的拓展 if ([response.actionIdentifier isEqualToString:@"input text"]) {//识别用户点击的是哪一个 action //假设点击了输入内容的 UNTextInputNotificationAction 把 response 强转类型 UNTextInputNotificationResponse *textResponse = (UNTextInputNotificationResponse*)response; //获取输入内容 NSString *userText = textResponse.userText; //发送 userText 给须要接收的方法 [ClassName handleUserText: userText]; }else{ } } completionHandler(); }
能够在手机「接收到推送以后、展现推送以前」对推送进行处理,更改、替换原有的内容。
使用了这个玩意,大家公司原有发送推送的 payload 能够彻底不变,而在客户端对接收到的内容(只有一条字符串)进行加工,从而适配 iOS 10 的展现效果(标题+副标题+内容)。
值得大家 App 充分发挥的是能够作如下事情:
不急,咱们先来介绍怎么
先在 Xcode 打开你的 App 工程,File - New - Target 而后添加这个:
而后会自动建立一个 UNNotificationServiceExtension 的子类 NotificationService,经过完善这个子类,来实现你的需求。
点开 NotificationService.m 会看到 2 个方法:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title]; self.contentHandler(self.bestAttemptContent); } - (void)serviceExtensionTimeWillExpire { self.contentHandler(self.bestAttemptContent); }
{ aps : { alert : "New Message", mutable-content : 1 }, encrypted-content : "#myencryptedcontent" }
首先须要添加 mutable-content : 1,这意味着此条推送能够被 Service Extension 进行更改
同时能够附加一条 encrypted-content,能够提取该内容进行替换
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { //用你的重编码方法对该内容进行更改 NSString *decryptedBody = [DecryptClass decrypt: request.content.userInfo[@"encrypted-content"]]; //建立新的 content 并添加修改过的 body UNMutableNotificationContent *newContent = [UNMutableNotificationContent new]; newContent.body = decryptedBody; //回调新的 content contentHandler(newContent); }
咱们先来看一下 iOS 10 默认的推送 UI。
包括「横幅、锁屏、通知中心 」三处,看起来差很少的样子。
推送内容中增图片、gif、audio、video。
在以上的三个界面均可以经过 3d-touch 触发。
先一块儿来看看效果。
打开 iOS Xcode Project - File - New - Target - iOS - Notification Service Extension - Next - Product Name 填写 yourPushNotificationService
- Finish
具体图示方法,在《中》里有详细的介绍。
添加文件。把大家定制的各类 media 文件拖拽至上一步系统自动生成的 yourPushNotificationService
文件夹下,勾上 copy to items,add to targets 必定要选择这个 Notification Service 做为 target,以下所示。
添加代码。在 2 中生成的 NotificationService.m
里添加代码:
-(void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; // 1.把推送内容转为可变类型 self.bestAttemptContent = [request.content mutableCopy]; // 2.获取 1 中自定义的字段 value NSString *urlStr = [request.content.userInfo valueForKey:@"your-attachment"]; // 3.将文件夹名和后缀分割 NSArray *urls = [urlStr componentsSeparatedByString:@"."]; // 4.获取该文件在本地存储的 url NSURL *urlNative = [[NSBundle mainBundle] URLForResource:urls[0] withExtension:urls[1]]; // 5.依据 url 建立 attachment UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:urlStr URL:urlNative options:nil error:nil]; // 6.赋值 @[attachment] 给可变内容 self.bestAttemptContent.attachments = @[attachment]; // 7.处理该内容 self.contentHandler(self.bestAttemptContent); }
先运行你的项目 target 使之在手机上安装,再运行 Notification Service 的 target,并选择在你的项目上运行该 Extension。此时可进行 Notification Service 代码的调试,即在 NotificationService.m
中打断点能够调试,可是在你的项目中的断点没法调试。
发送 payload 需依照下述格式:
{ aps : { alert : {...}, mutable-content : 1 //必须 } your-attachment : aPicture.png //必须 }
其中:
mutable-content : 1
说明该推送在接收后可被修改,这个字段决定了系统是否会调用 Notification Service 中的方法。your-attachment:是自定义的字段,key 能够自定义(你本身要记住),value 须要是一个完整的文件名(或 url,后续会详细解释),即你想要展现的文件。
手机接收后,在任一个能看到推送条目的界面对推送条目进行 3d-touch 强按均可以触发。(须要 iPhone 6s 及之后设备 & iOS 10)
提示:各类 media 文件大小有必定限制,图片、视频等过大都不会被展现,Apple 的意思是:对于图片,最大宽度也就和屏幕等宽,过大的图片没有意义;对于音频、视频等,彻底能够提供一个短期预览部分,更多的内容仍是须要用户点击推送进入 App 以后对完整的内容进行查看。但愿开发者听从这样的逻辑进行开发。
iOS 10 新增的另外一项 Extension,用于彻底自定义推送展现的 UI 界面,响应 Actions 的同时刷新该 UI。简单的说就是你能够把须要推送的内容(好比一条完整的新闻快讯,包括多条文字+图片的组合)所有放到一条推送里,用户点击了一个 Action(如赞、踩、关注、甚至评论等),在推送里马上刷新 UI(如展现加星动画、评论内容等)。
下图中日程表的 UI 彻底由开发者自定义,而且在点击了 Accept 以后,UI 当即发生了变化:
打开 iOS Xcode Project - File - New - Target - iOS - Notification Content - Next - Product Name 填写 yourPushNotificationContent
- Finish
系统会在 Xcode 工程目录中 自动生成 yourPushNotificationContent
文件夹,而且包含四个文件:NotificationViewController.h
、NotificationViewController.m
、MainInterface.storyboard
、Info.plist
。
NotificationViewController 继承自 UIViewController,并实现了 UNNotificationContentExtension 协议。
拖拖拽拽一个 UI 就出来了 ^。^
须要在这里让系统知道,哪一个 id 字段会触发你这个 extension。
高亮部分字段的值,须要跟 Notification Actions 的 category id 值同样,这样收到推送时,就会同时触发 Notification content + Notification actions。
同时这里也能够添加多个值,用于收到不一样的推送,展现相似的 UI。
好比接受聚会邀请和提醒聚会邀请,UI 相近,操做却不一样。
当你各类 UI 展现后,会发现存在 2 个问题。
是系统会自动展现一遍收到的推送内容,这部分极可能跟你的内容是重复的。
在 Info.plist 中添加以下字段,而且把值设为 YES 便可隐藏系统默认的展现。
是展现内容比较少的时候,系统仍然会以最大的界面展现出来,会露出不少空白部分。
- (void)viewDidLoad { [super viewDidLoad]; CGSize size = self.view.bounds.size; self.preferredContentSize = CGSizeMake(size.width, size.height/2); }
效果以下所示,仔细看你会发现存在小 bug,先展现了完整的高度,而后瞬间变成一半的高度,看起来有个高度适应的动画的样子。致使这种结果的缘由是系统准备展现推送的时候,尚未执行到你的代码(展现从系统层级到 App 层级的过程),这是苹果内部的机制所致。
这样系统层级会预先读取该数据,用于展现。固然有时候展现的内容不一样,须要的高度不一样,而这里只能设置成惟一的固定值。不过这也是现阶段苹果所能给你提供的可行方法了。
而后最终的展现效果以下,没有上面那个不舒服的高度调整动画了。
感受 Notification Content 的功能极其强大,有了它以后连 App 都不须要再启动了的样子(只要能合理的设计展现内容和操做),省去了用户每次为了一项简单操做都要进行「启动 App - 操做 - 切换到多任务界面 - 退出 App」这样的繁琐过程。本来用户看到推送可能不太有意愿去查看详细内容,如今他只须要很简单的操做就能快速的查看,推送的点开率应该会所以而大幅增长吧。究其如此便捷的缘由,Notification Service Extension 和 Notification Content 都是独立于项目的 target,收到推送后,系统会单独运行这两个 target,彻底不会在此时去启动 App 并执行 App 中大量的代码,童鞋们在调试的时候也能够注意这一点。