iOS10-UserNotifications

作者: linatan | 来源:发表于2016-07-24 17:08 被阅读14399次

1. 简介

  1. UserNotifications官方文档说明
  2. 内容丰富;可以获得用户是否同意推送等notification setting信息;提供trigger;app在前台可以捕捉并处理即将触发的推送
  3. 框架是UserNotifications.framework;支持UserNotificationsUI.framework自定义通知的UI展示

2. UserNotifications.framework

2.1 本地通知的trigger

  1. 新功能trigger,可以在特定条件触发,有三类:UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger、UNLocationNotificationTrigger
  2. UNTimeIntervalNotificationTrigger:一段时间后触发
//2min后提醒
UNTimeIntervalNotificationTrigger *trigger1 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:120 repeats:NO];
  1. 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);
  1. 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

  1. 本地推送和远程推送同时都可支持附带Media Attachments。不过远程通知需要实现通知服务扩展UNNotificationServiceExtension,在service extension里面去下载attachment,但是需要注意,service extension会限制下载的时间,并且下载的文件大小也会同样被限制。这里毕竟是一个推送,而不是把所有的内容都推送给用户。所以你应该去推送一些缩小比例之后的版本。比如图片,推送里面附带缩略图,当用户打开app之后,再去下载完整的高清图。视频就附带视频的关键帧或者开头的几秒,当用户打开app之后再去下载完整视频。
  2. attachment支持图片,音频,视频,附件支持的类型及大小
  3. 系统会在通知注册前校验附件,如果附件出问题,通知注册失败;校验成功后,附件会转入attachment data store;如果附件是在app bundle,则是会被copy来取代move
  4. media attachments可以利用3d touch进行预览和操作
  5. 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框架中对应UNNotificationActionUNNotificationCategory

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 注册通知过程

  1. UNUserNotificationCenter规划所有通知,管理和通知相关的行为;通过currentNotificationCenter获取单例
  2. 获取通知权限,通过requestAuthorizationWithOptions:completionHandler: 向用户请求打开权限; getNotificationSettingsWithCompletionHandler: 可以返回通知权限状态
  3. 设置UNMutableNotificationContent,通过通过categories配置可操作的通知
  4. 利用trigger设置通知出发的条件
  5. requestWithIdentifier:content:trigger:初始化UNNotificationRequest
  6. addNotificationRequest:withCompletionHandler: 注册通知
  7. 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

  1. 主要2个方法:接收通知,处理用户行为
  2. 通过userNotificationCenter:willPresentNotification:withCompletionHandler:将通知传递给前台运行的app
  3. 通过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

UserNotificationsUI文档

3.1 UNNotificationContentExtension

  1. 如何自定义通知界面
  2. wwdc-2016-rich-notifications-in-ios-10
  3. 注意:stackoverflow上答案
    自定义界面(来自网络)

4. UNNotificationServiceExtension

4.1 模拟发送remote notification

NWPusher

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的条件

  1. 远程通知展示alert给用户
  2. 远程通知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];

4.3.3 git代码地址

MyUserNotificationTestDemo

相关文章

  • iOS10-UserNotifications

    1. 简介 UserNotifications官方文档说明 内容丰富;可以获得用户是否同意推送等notificat...

网友评论

  • 无法登录原po的小聪聪_阿浪坚:图片存储那块处理的不错,整个推送定制讲的也可以
  • 晨星_风:添加Notification Service Extension来处理远程通知,下载消息中的图片的时候,是不是需要生成新的推送证书?我用老的推送证书是收不到图片的,只能显示标题和内容,而不调用新加的extension的处理图片的方法。新的推送证书推送的时候总是报deviceToken错误,卡在这里烦死了,求大神解答~
    JosephPoplar:我也是,NotificationService中的方法都不走
    晨星_风:@linatan 打开了,现在解决了一部分推送证书的问题。实际是用老的推送证书就可以收到消息,但是现在还有个问题是:在老的app可以收到消息,但是收不到图片,在extension的下载图片的方法打断点都不执行,标题副标题内容都是正常收到,没走extension的逻辑(mutable-content字段也是加了的);然而,另建一个test app,用同样的bundle id来安装test app,图片就是可以正常收到的,反复检查了好几遍工程设置,配置等等,没发现问题所在,包括重新添加extension到老的app里面,反复几次 都还是这样,无语了。
    linatan:有打开项目中的push notification么
  • 简书12138:使用NWPusher推送的时候 如果有mutable-content:1这个字段就接收不到推送了
  • dedenc:你好 很感谢你能无私分享 我下载你了demo 有个问题想请教下 为什么我打开工程会提示 我 [UNNotificationCategory categoryWithIdentifier:@"helloIdentifier" actions:@[action1,action2] minimalActions:@[action1,action2] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];

    这个方法提示我no know class method for selector 是不是我还需要导入其他的文件?万分感谢
    dedenc:@linatan 谢谢 我看到了 我换了个方法 看来是这个方法名没有
    linatan:@dedenc 这个是之前写的代码,现在这个方法已经改成+ (instancetype)categoryWithIdentifier:(NSString *)identifier actions:(NSArray<UNNotificationAction *> *)actions intentIdentifiers:(NSArray<NSString *> *)intentIdentifiers options:(UNNotificationCategoryOptions)options; 去掉minimalActions:就可以了
  • 红烧大鸡腿:为什么按照以上步骤真机测试的时候能收到推送,但是并没有UNNotificationAction实现该有 的效果,这是咋回事?
    红烧大鸡腿:@linatan 之前试过了,确实没有....
    linatan:有图片显示么?你可以连着真机调试下,看有没有下载图片
  • Joe_lisa: UIRemoteNotificationType type=UIRemoteNotificationTypeAlert |UIRemoteNotificationTypeBadge| UIRemoteNotificationTypeSound; 这些以前做通知的代码还有效吗,最近在改版的时候,发现以前做推送的出问题,不知道是不是,因为Use UserNotifications Framework's UNAuthorizationOptions for user notifications and registerForRemoteNotifications for receiving remote notifications instead.") __T 导致推送消息有故障
  • 范特西V:多谢这么详细的总结!根据你的教程,做了iOS10的推送的适配,并用development模式测试,能正常获取到DeviceToken,也能正常收到推送信息。。就是指定了 'sound' => 'push.caf',在iOS9收到推送能播放自定义的声音,但是在iOS10里就不起作用了,仍然是播放系统推送声音,不知你在测试的时候,有无在iOS10里遇到这个问题?
    alin_薄荷:请问你推送的声音问题解决了么 我也遇到同样的问题,10之前的会自动播放sound的文件,但是10之后的就还是系统的声音。
    linatan:@范特西V 这个没试过哎,push.caf本地有么,你试下,在extension中包装下 sound = [UNNotificationSound soundNamed:UILocalNotificationDefaultSoundName];
  • Marcusias:智行火车票(iOS 10) 有一个始终 只在锁屏界面显示的推送,在应用内也有在锁屏界面显示的开关,点击左滑都消除不掉,一直显示 ,请问有没有实现的思路?
    Marcusias:@等这姑娘老在我心里 是的
    等这姑娘老在我心里:是不是Passbook
  • 旧夏2014:setNotificationCategories:可以不调吗,调的话传nil可以吗
    linatan:@旧夏2014 远程推送证书分开发证书和线上证书的啊
    旧夏2014:@linatan 你用xcode8了吗,在推送的时候需要添加一个.entitlements的文件,然后里面有一个key:APS Environment value:development 那我发布的时候是不是value得用distribution
    linatan:@旧夏2014 可以啊,如果没有category,就没有action展示
  • pikacode::+1:
    linatan:@Pikacode :stuck_out_tongue_winking_eye:

本文标题:iOS10-UserNotifications

本文链接:https://www.haomeiwen.com/subject/uoxgjttx.html