WWDC session - Notifications 学习总结,如有不妥之处,望请指正🙏🙏
最初接触通知就直接用的三方,导致对于通知的整个api和它的概念都有一些不甚了解。也只是会用,能完成正常的需求。
然后就想着能够系统的从头从新了解一些通知,直到最近时间有所空闲才把从11年开始的session都翻出来看了一遍,顺便做了一下整理。
如果也有像我这种对通知一知半解的童鞋,建议不要直接集成三方的推送,可以使用上面的pusher工具,这样对于整个messagePayload的格式都能有个详细的了解,结合之后反映到api上学习效果更佳。
目录
一.Notifications 简介
二.Notifications 结构
三.Push Notifications API
· 注册通知
· deviceToken是什么
· Message payload 格式
· 接收payload
四.Local Notifications
五.iOS9有什么改变
· new category action - text input (推送消息的快捷回复)
· text input action 的 payload格式
· 接收text input category action 响应
· new provider api (新的基于HTTP/2的APNs Protocol)
六.iOS 10 User Notifications
· UI 变化
· User Notifications api
· UNNotificationSound对象设置推送声音
· 推送的媒体附件
· 推送触发器
· 推送请求(取消和更新通知)
· Notifications Delegate
· Notification Action (可响应操作的通知)
· Notification Service Extension(可变通知扩展)
· Notifications Content Extension(自定义通知UI)
七. Text input action之自定义inputAccessoryView
八.小结
1.Notifications 简介
什么是Notifications
那么Notifications到底是什么呢,其实就是一个信息弹窗,用于反应某些事件的。
为什么要使用Notifications
产品为什么大多都要加上Notifications功能,一方面确实能在app不处于运行状态时也能发布一些具有时效性的事件,另一方面从运营方面考虑通知也是一个app保活的手段。
推送通知与poll(轮询)的区别
push是server驱动,而且是及时的
poll是app驱动,而且相对延时的
2.Notifications 结构
推送通知又是如何实现的呢?
推送通知要借助于苹果的
Apple Push Notifications service
服务器,简称APNs
发给我们的设备。那么
APNs
服务器怎么知道要发给哪一台设备呢?这里就由设备的deviceToken
来标识。有了
APNs
,有了我们设备的deviceToken
,还需要一个连接我们app和APNs
的provider,这里就是我们服务器了。如上图所示,设备获取到
deviceToken
,然后发送给我们自己的服务器,服务器添加payload
json与deviceToken
一起发送给苹果的APNs
服务器,然后由APNs
服务器将payload
json通知给目标设备。
3.Notifications API
苹果一直对于各种权限要求的比较严格,我们的app既然要使用Notifications的功能,那么就要获取到用户的授权信息,获取授权就要申请注册通知。
iOS10之前的通知注册由UIApplication
来做。
注册通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
}else {
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
}
return YES;
}
注册时可以选用你需要的NotificationTypes
iOS8之后使用新的注册通知的方式,如果不需要适配iOS7,则可以抛弃registerForRemoteNotificationTypes
方法。
注册通知后的情况
(1). 成功注册后会执行该回调方法,在此方法中可以获取到deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@"successful--%@",deviceToken);
}
(2). 注册失败后会执行该回调方法
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}
注意,模拟器不支持推送通知。
deviceToken是什么
- 它是一个设备上某个app的唯一标识,有了它才能将消息推送到指定的设备上的某个app。
·· 它是从UDID中分离出来的- 它是会发生改变的(系统升级,app删除重装)
·· 在app每次launch的时候都要调用注册api确保获取最新的deviceToken
·· 不用做deviceToken的缓存
Message payload 格式
注册成功,获取了用户授权之后我们需要关心的内容就可以暂时先放到Message payload
上,Message payload
决定了一条推送的内容,声音,角标等等属性。
Message payload
格式介绍略多,并且反应到不同iOS版本的字段还有些不同,在这里是大部分通用字段的介绍,在下文中的iOS9和iOS10的介绍里面也会有针对版本特性的字段的介绍。
{
"aps" : {
"alert" : {
"body" : "test-body",
"title" : "test-title"
}
"badge": 1,
"sound": "Jingle.aiff"
},
"acme1" : "conversation"
}
Message payload
就是一个json串,格式如上所示。其中aps
字典包含了声音,角标,内容的key-value,aps
字典中所有的key都是可选的。
(1)badge角标格式
badge的值为integer,设置该值之后,应用右上角会出现数字角标。
{
"aps" : {
"badge": 1
}
}
清除角标数则可将badge
值设置为0
{
"aps" : {
"badge": 0
}
}
(2)sound声音格式
sound的值可以是bundle中的音频文件名称。
{
"aps" : {
"sound": "Jingle.aiff"
}
}
如果使用"default",则接收到推送时为系统默认声音。
{
"aps" : {
"sound": "default"
}
}
其中接收到推送的震动是默认自带的,不需要使用键值控制。
(3)alert内容格式
alert的值苹果推荐使用字典来配置,其可用的key 有
key | desc | type | version |
---|---|---|---|
title |
推送的标题 | String |
8.2 |
body |
推送的内容 | String |
|
title-loc-key |
本地化推送标题的key,可以使用%@ 和 %n$@ 格式化配置从title-loc-args 数组中获取变量值 |
String or null
|
8.2 |
title-loc-args |
本地化推送标题key对应的变量值数组 | 字符串数组 or null
|
8.2 |
action-loc-key |
action 按钮标题本地化配置的key |
String or null
|
- |
loc-key |
本地化消息的key,可以使用%@ 和 %n$@ 格式化配置从loc-args 数组中获取变量值 |
String |
- |
loc-args |
本地化消息key对应的变量值数组 | 字符串数组 | - |
launch-image |
bundle中的一个图片,可以有图片的后缀名,也可以没有。<br /> 如果设置了这个键值,那么用户点击推送视图打开app时,LaunchImage 就会被指定为该图片。<br /> 如果没有指定该值,则仍然使用app默认在info.plist 中使用UILaunchImageFile 配置的图片。 |
String |
- |
(4)推送本地化
为了有针对性的对不同地区,不同语言做推送的本地化,可以使用alert
中的一些本地化key。
推送的本地化有两种方式:
A - 服务器提供--需要将用户设备当前的语言设置传递给服务器。
当前设备的语言偏好设置获取可以使用 NSLocale
的preferredLanguages
属性来获取。
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
const char *langStr = [preferredLang UTF8String];
需要注意的是:用户可以修改语言的系统偏好设置,这样就要监听语言改变的NSCurrentLocaleDidChangeNotification
通知了,在系统语言发生变化时上报给服务器。
这样的好处是服务器想推什么推什么。
B - 使用Localizable.strings
文件配置--需要将本地化的消息事先配置好,灵活性相较于服务器提供有所欠缺。
Localizable.strings
中配置类似如下的键值对:
"GAME_PLAY_REQUEST_FORMAT" = "%@ and %@ have invited you to play Monopoly";
Message payload
中alert的格式如下:
{
"aps" : {
"alert" : {
"loc-key" : "GAME_PLAY_REQUEST_FORMAT",
"loc-args" : [ "Jenna", "Frank"]
}
}
}
这样就可以在app中做推送的本地化配置了。
接收payload
- 如果你的app在运行中,你只能通过以下方法获取。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"推送消息===%@",userInfo);
//处理传过来的推送消息
}
- 如果你的app不在运行状态,当点击弹窗视图时,只能通过以下方法获取到通知的payload。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
...
return YES;
}
可以通过UIApplicationLaunchOptionsRemoteNotificationKey
从launchOptions
中获取到payload。
注意点
需要注意这个fetchCompletionHandler
方法
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
如果实现了这个方法,那么- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
方法将不会被执行。
didReceiveRemoteNotification:fetchCompletionHandler:
方法有什么作用呢?
按照苹果官方的解释这个代理方法,为开启了remote-notification
background mode的app提供了一个机会去获取适当的新数据,来响应即将到来的远程通知。
也就是说苹果给了一个在程序在后台运行时能继续跑代码的方法。(注意程序需要在后台运行中)。
4.Local Notifications
Local Notification 和 Push Notification有什么区别呢?
Push Notification是由服务器发出的,Local Notification是由app发出的。
Push Notification是一次性的,Local Notification则是可以事先设定的,而且是可重复的。
如果你要实现一个闹铃的提醒或者是一个备忘提醒,那么就非常适合使用Local Notification来实现了。
Local Notification API
- Badge (角标)
NSInteger applicationIconBadgeNumber
- Alerts
NSString *alertBody 通知内容
NSString *alertTitle 标题 // 8.2
NSString *category // 8.0
BOOL hasAction
NSString *alertAction
NSString *alertLaunchImage 自定义LaunchImage
- Sound (声音)
NSString *soundName 推送声音
- Scheduling (设定)
NSDate *fireDate 推送时间
NSTimeZone *timeZone 时区
- Repeating (重复设置)
NSCalendarUnit repeatInterval
NSCalendar *repeatCalendar
- Metadata
NSDictionary *userInfo 推送payload
直接上代码演示
UILocalNotification *note = [[UILocalNotification alloc] init];
note.applicationIconBadgeNumber = 3; // 角标
note.alertBody = @"test body"; // 内容
note.alertTitle = @"test title"; // 标题
note.soundName = @"test.aiff"; // 自定义推送声音
// note.soundName = UILocalNotificationDefaultSoundName; // 默认声音
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComos = [[NSDateComponents alloc] init];
[dateComos setDay:10];
[dateComos setMonth:6];
[dateComos setYear:2017];
[dateComos setHour:11];
note.fireDate = [calendar dateFromComponents:dateComos]; // 推送发出的时间
// note.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0];
note.timeZone = [calendar timeZone]; // 时区
note.repeatInterval = NSCalendarUnitDay; // 每天这个时间重复发出
/*
常用的key如下:
NSCalendarUnitEra ,
NSCalendarUnitYear ,
NSCalendarUnitMonth ,
NSCalendarUnitDay ,
NSCalendarUnitHour ,
NSCalendarUnitMinute ,
NSCalendarUnitSecond ,
NSCalendarUnitWeekday ,
NSCalendarUnitWeekdayOrdinal ,
NSCalendarUnitQuarter ,
NSCalendarUnitWeekOfMonth ,
NSCalendarUnitWeekOfYear ,
NSCalendarUnitYearForWeekOfYear ,
NSCalendarUnitNanosecond ,
NSCalendarUnitCalendar ,
NSCalendarUnitTimeZone
*/
note.repeatCalendar = [NSCalendar currentCalendar];
// 使用scheduleLocalNotification方法可以在指定的fireDate发送本地通知
[[UIApplication sharedApplication] scheduleLocalNotification:note];
// 使用presentLocalNotificationNow方法则会忽略fireDate直接发送该通知
//[[UIApplication sharedApplication] presentLocalNotificationNow:note];
// 当然可以使用cancelLocalNotification取消掉某个通知的发布,如果该通知已经弹出,调用该方法也会dismiss该通知。
//[[UIApplication sharedApplication] cancelLocalNotification:note];
接收本地推送
如果app在运行,则会执行下面的方法
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSLog(@"%@",notification.userInfo);
}
如果app不在运行,则可以在launchOptions
中通过UIApplicationLaunchOptionsLocalNotificationKey
获取到本地通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *note = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
return YES;
}
5.iOS9有什么改变
new category action - text input (推送消息的快捷回复)
iOS9新添加了一个UIUserNotificationAction
的type -> UIUserNotificationActionBehaviorTextInput
在注册通知setting的时候可以添加此UIUserNotificationAction
,来实现通知消息的快捷回复,如下图:
示例代码如下:
// 声明一个操作
UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];
action.title = @"回复";
action.identifier = @"test-replay-action";
action.behavior = UIUserNotificationActionBehaviorTextInput;
// 声明一个操作分类
UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
category.identifier = @"test-replay"; // 注册操作分类的identifier
[category setActions:@[action] forContext:UIUserNotificationActionContextDefault];
NSSet *set = [NSSet setWithObjects:category, nil];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:set];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
如代码中所示,需要首先声明一个UIUserNotificationActionBehaviorTextInput
类型的UIUserNotificationAction
。
然后将其添加到已经注册了identifier
的操作分类中,然后在UIUserNotificationSettings
设置此分类。
调用application
的注册方法,将UIUserNotificationSettings
配置进去,至此将UIUserNotificationActionBehaviorTextInput
快捷回复的操作注册完毕。
text input action 的 payload格式
之后在Message payload中添加category
字段,category
字段的value值为之前注册的操作分类的identifier,即category.identifier
。
{
aps = {
alert = {
body = "test action";
title = "test action title";
};
badge = 1;
category = "test-replay";
sound = default;
}
接收text input category action 响应
push notifications可以在下面这个方法中接收输入操作
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
NSLog(@"identifier---%@---userInfo---%@---responseInfo---%@",identifier,userInfo,responseInfo);
}
local notifications 可以在这个方法中接收输入操作
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
NSLog(@"identifier---%@---notification---%@---responseInfo---%@",identifier,notification,responseInfo);
}
由identifier
字段来区分不同的action
log输出结果如下:
identifier---test-replay-action---userInfo---{
aps = {
alert = {
body = "test action";
title = "test action title";
};
badge = 1;
category = "test-replay";
sound = default;
};
}---responseInfo---{
UIUserNotificationActionResponseTypedTextKey = "\U54c8\U54c8\U54c8\U54c8\U54c8";
}
new provider api
在iOS9中苹果升级了APNs push Protocol,这个新版本的协议基于HTTP/2和JSON,相比于旧的二进制协议,新的协议有了巨大改进。
新的provider api在前端开发中涉及不多,在这里就不再细说,有兴趣的可以点击以下链接进行细节研究。
官方Binary Provider API
WWDC session 720
6.iOS 10 User Notifications
UI 变化
在iOS10中最直观的改变就是UI的改变,一个通知中包含了标题,子标题,内容,以及媒体附件。如下图:
User Notifications api
在iOS10,苹果将Notifications进行了重构。
从iOS10开始UINotification
已全部被标记为废弃,如果你的app不需要支持更早的版本,你就可以使用最新的User Notifications Framework
了。
直接导入#import <UserNotifications/UserNotifications.h>
即可使用。
与之前的api相比较,UN框架将通知的初始化与发送做了更加细化的重构。
之前几乎所有的内容,触发,是否重复 等等属性全部都在UINotification
中设置。
UN框架则将其细化为大致如下内容:
- 新的注册api
- 通知的内容 UNNotificationContent
,包含推送内容的一些基本属性设置
- 通知触发器 UNNotificationTrigger
,分为
· 推送触发器UNPushNotificationTrigger
· 时间触发器UNTimeIntervalNotificationTrigger
· 日期触发器UNCalendarNotificationTrigger
· 以及位置触发器UNLocationNotificationTrigger
- 通知请求 UNNotificationRequest
,请求中包含通知内容以及通知触发器。
- 最后将通知请求添加到推送中心,交由通知中心调度。
示例代码如下:
// 注册
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
// 声明一个通知content
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"hello world";
content.subtitle = @"test notifications";
content.body = @"hello body";
UNNotificationSound *sound = [UNNotificationSound defaultSound];
content.sound = sound;
// 初始化一个图片附件
NSString *picAttachMentIdentifier = @"picAttachMentIdentifier";
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"IMG_3836" ofType:@"JPG"]];
NSError *error ;
UNNotificationAttachment *picAttachMent = [UNNotificationAttachment attachmentWithIdentifier:picAttachMentIdentifier URL:url options:nil error:&error];
/*注意如果无法获取到file url 的data,UNNotificationAttachment则会返回nil*/
content.attachments = @[picAttachMent];
// 声明一个时间触发器
UNTimeIntervalNotificationTrigger *timerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
// 声明一个通知请求
NSString *requestIdentifier = @"requestIdentifier";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:timerTrigger];
// 将通知请求交给推送中心调度,通知中心会在合适时机发布该通知。
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
}];
由上述调用代码可以看出来,这次的重构将之前在几乎都是在一个类中配置的各种属性基本都给分离出来了。
通知payload相关的基本都在UNMutableNotificationContent
类中,包括:
attachments // 媒体附件
badge // 角标数值
body // 内容
subtitle // 子标题
title // 标题
categoryIdentifier // 操作分类id
launchImageName // 推送唤醒时的launchImage 图片
sound // 声音
userInfo // 额外附带信息
threadIdentifier // 通知request 的线程id
sound 推送声音
推送声音的设置现在不在是一个字符串了,需要给content传递一个UNNotificationSound
对象。
// 默认推送声音
UNNotificationSound *sound = [UNNotificationSound defaultSound];
content.sound = sound;
// 自定义推送音效
// UNNotificationSound *sound = [UNNotificationSound soundNamed:@"sms-received1.caf"];
如果创建本地推送时,不给content设置sound属性的值,则推送默认没有声音。
attachments 媒体附件
媒体附件支持的格式以及大小如下图所示:
如果为不支持的文件类型,或者大小超过了。则返回空
attachments
对象。
Trigger触发器
通知的发送需要给这条通知设置相应的触发器,iOS10之后苹果提供了以下的触发器:
UNTimeIntervalNotificationTrigger
时间触发器,该触发器可以设置通知什么时候发出,是否重复发送。
UNTimeIntervalNotificationTrigger *timerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
UNCalendarNotificationTrigger
日期触发器可以设置具体的日期的通知提醒。
NSDateComponents *dateComos = [[NSDateComponents alloc] init];
[dateComos setDay:10];
[dateComos setMonth:6];
[dateComos setYear:2017];
[dateComos setHour:11];
UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComos repeats:YES];
UNLocationNotificationTrigger
地点触发器可以在用户进入某个区域时给用户通知提醒。
CLRegion *region ;
UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];
推送请求(取消和更新通知)
一个UNNotificationRequest
对象中包含了一条推送的内容和触发器,将推送请求对象交给通知中心之后,这条通知才会在通知中心的调度下在合适的触发时机下发出。
而UNNotificationRequest
的作用又是什么呢?
在iOS10中,可以通过UNNotificationRequest
来取消或者更新通知。而这个取消和更新的关键就在于UNNotificationRequest
的requestIdentifier
属性。
取消未发出的通知可以使用以下方法:
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[@"com.wkj.requestIdentifie"]];
取消已发出的通知可以使用以下方法:
[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
更新未发出的通知可以使用以下方法:
UNTimeIntervalNotificationTrigger *newTimerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:20.0 repeats:NO];
UNNotificationRequest *newRequest = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:newTimerTrigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:newRequest withCompletionHandler:^(NSError * _Nullable error) {
}];
经测试通过requestIdentifier
更新通知的方式分两种情况:
- 对于未发出的推送,只能更新通知的触发器,如果重新设置了
request
的content
。则原通知的content不会进行更新,且新的触发器失效。 -
对于已发出的推送,可以重新设置触发器和内容。如下图:
神奇小贴士
注意UNNotificationRequest
对象的requestIdentifier
属性,不能设置为@"",貌似会变砖,有多余设备的同志试验后请告知结果( ´థ౪థ)σ。
Notifications Delegate
对比之前的注册方法,iOS10之前,使用UIApplication
进行注册操作,默认通知的代理回调需要在AppDelegate
中处理。
而iOS10则需要自己来设置代理,可以在注册结果回调的block中根据回调结果,做代理的设置。
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
}
}];
灵活的设置代理方法,可以将通知的代理方法从AppDelegate
剥离出去。
UNUserNotificationCenterDelegate的两个代理方法
- 处于前台时的代理回调方法
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge;
completionHandler(presentationOptions);
}
当app处于前台时,会执行该方法,在此方法中可以过滤将要显示的通知的一些设置选项,例如如果处于前台时收到通知,将通知的角标设置选项给过滤掉。
如果想要在前台时,显示通知的alert弹框则需要注意,一定要执行completionHandler()
。
不执行completionHandler()
的话是不会在前台时显示通知的alert弹框的。
神奇小贴士:
如果没有实现该方法,仍然想要通知在前台显示,则可以设置UNNotificationContent
的shouldAlwaysAlertWhileAppIsForeground
属性。
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
[content setValue:@YES forKey:@"shouldAlwaysAlertWhileAppIsForeground"];
- 处于后台时的代理回调方法
当app处于运行状态时,不管是本地还是远程通知,当用户点击推送的alert弹窗时,则会执行该方法。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void(^)())completionHandler {
NSLog(@"notification response : %@",response);
}
当app不在运行状态时,仍然只能在application:didFinishLaunchingWithOptions:
中获取到通知内容。
推送通知获取:
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
本地通知获取:
NSDictionary *userInfoLocal = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
神奇小贴士:
UIApplicationLaunchOptionsLocalNotificationKey
在iOS10中被标记为废弃状态,被建议使用userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
方法替代。
但是当app不在运行状态时,此方法是不会被执行的,如果想要在app不在运行状态时,仍然响应本地通知相关事件的话,还是只能使用UIApplicationLaunchOptionsLocalNotificationKey
获取。
Notification Action (可响应操作的通知)
在上文中介绍过iOS9的action,与之前的操作相类似,自定义通知的
Action
需要实现注册,套路与之前版本的也类似。
在iOS10里面,Action
分两种,一种是UNNotificationAction
,另外一种是UNTextInputNotificationAction
。示例代码如下:
// 默认action
UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:@"iOS10-删除" title:@"删除" options:UNNotificationActionOptionDestructive];
// 输入框action
UNTextInputNotificationAction *textInputNotificationAction = [UNTextInputNotificationAction actionWithIdentifier:@"iOS10-replay" title:@"回复" options:UNNotificationActionOptionAuthenticationRequired textInputButtonTitle:@"test" textInputPlaceholder:@"placeholder"];
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"iOS10-category-identifier" actions:@[action , textInputNotificationAction] intentIdentifiers:nil options:UNNotificationCategoryOptionCustomDismissAction];
NSSet *set = [NSSet setWithObject:category];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:set];
使用也与之前的类似,远程推送在payload中添加category
字段,value值为category初始化时填写的identifier
。
本地推送同样给content设置categoryIdentifier
:
content.categoryIdentifier = @"iOS10-category-identifier";
其中UNTextInputNotificationAction
初始化参数中的textInputButtonTitle
为输入框右侧操作按钮的标题,textInputPlaceholder
参数为输入框的占位提示文字。效果如下图所示:
接收action的响应操作
action的操作响应可以在下面这个方法的UNNotificationResponse
中获取
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
completionHandler();
}
UNNotificationResponse
结构如下图所示:
如果是
UNTextInputNotificationAction
的响应,则返回的response对象类型为UNTextInputNotificationResponse
,用户输入的内容可由UNTextInputNotificationResponse
的userText
属性获取。
Notification Service Extension(可变通知扩展)
Notification Service Extension是iOS10新增的一个Extension,用于增加或者替换远程推送内容的。
反映到实际开发上:
-
Notification Service
可以解决推送敏感内容的端到端加密(End-to-end encryption
) - 也可以给远程推送添加本地的媒体文件
如何使用Notification Service Extension实现修改推送内容,添加本地媒体文件
Advanced Notifications 之Notifications Content Extension(自定义通知UI)
除了使用系统默认的Notification's UI,苹果还提供了Notifications Content Extension
方便开发者进行UI的自定义。如下图所示:
如何使用Notification Content Extension实现自定义推送UI
Text input action之自定义inputAccessoryView
系统默认的Text input action只有一个输入框,一个右侧的按钮。如果想要修改通知Text input action唤起的inputAccessoryView
怎么办呢。
很简单,这里并不会用到新的api。
- 1、重写canBecomeFirstResponder方法
-(BOOL)canBecomeFirstResponder{
return YES;
}
- 2、重写inputAccessoryView的getter方法返回自定义的inputAccessoryView
-(UIView *)inputAccessoryView{
return customInputView;
}
之后记得在下面这个方法中这么用:
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion{
if ([response.actionIdentifier isEqualToString:@"iOS10-replay"] ) {
[self becomeFirstResponder];
[self.textFiled becomeFirstResponder];
}
completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
效果如下图:
小结
从最初的push一路看下来发现,苹果几乎每年都会添加一些新的feature。每次改动不多,而具体怎么利用这些特性,就靠开发者各显神通了。
为了最大程度的保持app用户的活跃,我们最常用的方式就是经由APNs服务器发送remote push。
对于一些偏旅游推荐,景区介绍类的app,利用好地点触发器直接进行针对性推荐也是非常提升用户体验的。
除了这些,还有iOS8之后提供的PushKit,对于开启了voip通道的IM应用来说,直接使用PushKit唤醒,拉取离线消息,生成local push的方式对其体验的提升也是非常大的。
参考链接:
session 707 Introduction to Notifications--2016
session 708 Advanced Notifications--2016
session 724--2016
session 720--2015
session 517--2011