iOS10-UserNotifications
2016-07-24 本文已影响14399人
linatan
1. 简介
- UserNotifications官方文档说明
- 内容丰富;可以获得用户是否同意推送等notification setting信息;提供trigger;app在前台可以捕捉并处理即将触发的推送
- 框架是UserNotifications.framework;支持UserNotificationsUI.framework自定义通知的UI展示
2. UserNotifications.framework
2.1 本地通知的trigger
- 新功能trigger,可以在特定条件触发,有三类:UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger、UNLocationNotificationTrigger
- UNTimeIntervalNotificationTrigger:一段时间后触发
//2min后提醒
UNTimeIntervalNotificationTrigger *trigger1 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:120 repeats:NO];
- UNCalendarNotificationTrigger :调用triggerWithDateMatchingComponents:repeats: 进行注册;时间点信息用 NSDateComponents
//每周日早上 8:00 提醒
NSDateComponents *components = [[NSDateComponents alloc] init];
components.weekday = 1;
components.hour = 8;
UNCalendarNotificationTrigger *trigger2 = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];
//NSDateComponets的注意点,有了时间并不能得到weekday
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = 11;
dateComponents.month = 7;
dateComponents.year = 2016;
//输出结果是NSDateComponentUndefined = NSIntegerMax
NSLog(@"%td", dateComponents.weekday);
//根据calendar计算出当天是周几
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *date = [gregorianCalendar dateFromComponents:dateComponents];
NSInteger weekday = [gregorianCalendar component:NSCalendarUnitWeekday fromDate:date];
NSLog(@"%td", weekday);
dateComponents.weekday = weekday;
NSLog(@"%@", dateComponents);
- UNLocationNotificationTrigger:调用triggerWithRegion:repeats:进行注册,地区信息使用CLRegion,可以配置region属性 notifyOnEntry和notifyOnExit,是在进入地区、从地区出来或者两者都要的时候进行通知
//圆形区域,进入时候进行通知
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(37.335400, -122.009201);
CLCircularRegion* region = [[CLCircularRegion alloc] initWithCenter:center
radius:2000.0 identifier:@"Headquarters"];
region.notifyOnEntry = YES;
region.notifyOnExit = NO;
UNLocationNotificationTrigger* trigger = [UNLocationNotificationTrigger
triggerWithRegion:region repeats:NO];
2.2 Content
UNNotificationContent:属性readOnly
UNMutableNotificationContent:属性有title、subtitle、body、badge、sound、lauchImageName、userInfo、attachments、categoryIdentifier、threadIdentifier
本地消息内容 | 内容限制大小 | 展示 |
---|---|---|
title | NSString | 限制在一行,多出部分省略号 |
subtitle | NSString | 限制在一行,多出部分省略号 |
body | NSString | 通知栏出现时,限制在两行,多出部分省略号;预览时,全部展示 |
注意点: body中printf风格的转义字符,比如说要包含%,需要写成%% 才会显示,\同样
notification
2.2.1 attachments
- 本地推送和远程推送同时都可支持附带Media Attachments。不过远程通知需要实现通知服务扩展UNNotificationServiceExtension,在service extension里面去下载attachment,但是需要注意,service extension会限制下载的时间,并且下载的文件大小也会同样被限制。这里毕竟是一个推送,而不是把所有的内容都推送给用户。所以你应该去推送一些缩小比例之后的版本。比如图片,推送里面附带缩略图,当用户打开app之后,再去下载完整的高清图。视频就附带视频的关键帧或者开头的几秒,当用户打开app之后再去下载完整视频。
- attachment支持图片,音频,视频,附件支持的类型及大小
- 系统会在通知注册前校验附件,如果附件出问题,通知注册失败;校验成功后,附件会转入attachment data store;如果附件是在app bundle,则是会被copy来取代move
- media attachments可以利用3d touch进行预览和操作
- attachment data store的位置?利用代码测试 获取在磁盘上的图片文件作为attachment,会发现注册完通知后,图片文件被移除,在app的沙盒中找不到该文件在哪里; 想要获取已存在的附件内容,文档中提及可以通过UNUserNotificationCenter中方法,但目前文档中这2个方法还是灰的
getDataForAttachment:withCompletionHandler:
getReadFileHandleForAttachment:withCompletionHandler:
sound notification
2.2.2 代码片段示例
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"Hello 5 seconds!" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:@"Hello_message_body" arguments:nil];
content.subtitle = [NSString localizedUserNotificationStringForKey:@"good day" arguments:nil];
content.sound = [UNNotificationSound defaultSound];
//NSURL *url = [[NSBundle mainBundle] URLForResource:@"image_1" withExtension:@"jpeg"];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"xiongben" withExtension:@"gif"];
UNNotificationAttachment *attch = [UNNotificationAttachment attachmentWithIdentifier:@"photo" URL:url options:nil error:nil];
content.attachments = @[attch];
//......
2.3 actions
与UILocalNotification相似,也有action和category,在UserNotifications框架中对应UNNotificationAction和UNNotificationCategory
2.3.1 代码片段示例
UNNotificationAction *enterAction = [UNNotificationAction actionWithIdentifier:@"enterApp" title:@"进入应用" options:UNNotificationActionOptionForeground];
UNNotificationAction *ingnoreAction = [UNNotificationAction actionWithIdentifier:@"ignore" title:@"忽略" options:UNNotificationActionOptionDestructive];
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"helloIdentifier" actions:@[enterAction,ingnoreAction] minimalActions:@[enterAction,ingnoreAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObjects:category, nil]];
2.4 注册通知过程
- UNUserNotificationCenter规划所有通知,管理和通知相关的行为;通过currentNotificationCenter获取单例
- 获取通知权限,通过requestAuthorizationWithOptions:completionHandler: 向用户请求打开权限; getNotificationSettingsWithCompletionHandler: 可以返回通知权限状态
- 设置UNMutableNotificationContent,通过通过categories配置可操作的通知
- 利用trigger设置通知出发的条件
- requestWithIdentifier:content:trigger:初始化UNNotificationRequest
- addNotificationRequest:withCompletionHandler: 注册通知
- removePendingNotificationRequestsWithIdentifiers: 或者removeAllPendingNotificationRequests 可以取消通知
2.4.1 代码示例
//authorization
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted)
{
NSLog(@"授权成功");
}
}];
//regitser
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"Hello 5 seconds!" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:@"Hello_message_body" arguments:nil];
content.subtitle = [NSString localizedUserNotificationStringForKey:@"good day" arguments:nil];
content.sound = [UNNotificationSound defaultSound];
// Deliver the notification in ten seconds.
UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"FiveSecond"
content:content
trigger:trigger];
// Schedule the notification.
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if(error)
{
NSLog(@"%@",error);
}
}];
2.5 UNUserNotificationCenterDelegate
- 主要2个方法:接收通知,处理用户行为
- 通过userNotificationCenter:willPresentNotification:withCompletionHandler:将通知传递给前台运行的app
- 通过userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:将用户对通知响应结果告诉app
2.6 Structures
Structures | desc |
---|---|
UNAuthorizationOptions | badge、sound、alert、carPlay(在行车模式下也可以展示通知) |
UNNotificationActionOptions | autheticationRequired、destructive、foreground |
UNNotificationPresentationOptions | badge、sound、alert |
3. UserNotificationsUI.framework
3.1 UNNotificationContentExtension
- 如何自定义通知界面
- wwdc-2016-rich-notifications-in-ios-10
- 注意:stackoverflow上答案
自定义界面(来自网络)
4. UNNotificationServiceExtension
4.1 模拟发送remote notification
4.1.1 app注册远程通知,获取deviceToken
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(nonnull NSData *)deviceToken
{
NSLog(@"deviceToken:%@",deviceToken);
NSString *deviceTokenSt = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""]
stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"deviceTokenSt:%@",deviceTokenSt);
}
4.2 调用到ServiceExtension的条件
- 远程通知展示alert给用户
- 远程通知aps json字段中必须包含mutable-content键,并且值为1
4.3 主要处理函数
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
self.contentHandler(self.bestAttemptContent);
}
在该方法下载附件,或者做其他处理变动,可以做到在通知到达用户手机前做修改
4.3.1 aps内容
{
"aps":
{
"alert":
{
"title":"hello",
"subtitle" : "Session 01",
"body":"it is a beautiful day"
},
"category":"helloIdentifier",
"badge":1,
"mutable-content":1,
"sound":"default",
"image":"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=200&sharp=30"
}
}
4.3.2 示例代码
在上述didReceiveNotificationRequest: withContentHandler:方法中对image键值路径请求数据下载到本地,然后通过url设置attach
- (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];
NSDictionary *apsDic = [request.content.userInfo objectForKey:@"aps"];
NSString *attachUrl = [apsDic objectForKey:@"image"];
NSLog(@"%@",attachUrl);
//下载图片,放到本地
UIImage * imageFromURL = [self getImageFromURL:attachUrl];
//获取document目录
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES );
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
NSLog(@"document path: %@",documentsDirectoryPath);
NSString *localPath = [self saveImage:imageFromURL withFileName:@"MyImage" ofType:@"png" inDirectory:documentsDirectoryPath];
if (localPath && ![localPath isEqualToString: @""])
{
UNNotificationAttachment *attch= [UNNotificationAttachment attachmentWithIdentifier:@"photo"
URL:[NSURL URLWithString:[@"file://" stringByAppendingString:localPath]]
options:nil
error:nil];
if(attch)
{
self.bestAttemptContent.attachments = @[attch];
}
}
self.contentHandler(self.bestAttemptContent);
}
//另一种下载方式
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:attachUrl];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url
completionHandler:^(NSURL * _Nullable location,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
// 将临时文件剪切或者复制Caches文件夹
NSFileManager *mgr = [NSFileManager defaultManager];
// AtPath : 剪切前的文件路径
// ToPath : 剪切后的文件路径
[mgr moveItemAtPath:location.path toPath:file error:nil];
if (file && ![file isEqualToString: @""])
{
UNNotificationAttachment *attch= [UNNotificationAttachment attachmentWithIdentifier:@"photo"
URL:[NSURL URLWithString:[@"file://" stringByAppendingString:file]]
options:nil
error:nil];
if(attch)
{
self.bestAttemptContent.attachments = @[attch];
}
}
self.contentHandler(self.bestAttemptContent);
}];
[downloadTask resume];