资讯专栏INFORMATION COLUMN

iOS 10 推送 UNNotificationContent 与 UNNotificationSe

huhud / 2526人阅读

摘要:相信苹果是为了让推送的信息面板风格整体不会太杂乱。是一个媒体文件的通知,在苹果的实现流程上,是它在中将附件信息整理打包给。苹果再三强调推送面板上的交互要尽可能的简洁易用。

前言

随着人们对手机的依赖性越来越高,对于从手机获取信息也有了更多的要求。推送就是一项不可忽视的方案,它可以在用户没有打开APP下的情况下将信息及时的推送给用户。推送功能在运营上也有极为重要的意义,关乎到 用户体验留存 等问题。应当给予相当的重视。

iOS 系统上的推送经历了多年的发展,已经从最初那简单的信息展示 发展到现在可以支持更加丰富内容展示,图片,视频,音乐等等。用户可以在通知面板上查看详情,直接进行交互,而不用打开APP。

开发者可以为自己的 APP 进行界面的量身定制( NotificationContentExtension ),也可以在收到通知时截获通知内容,进行修改( NotificationServiceExtension )。

这篇文章是对 UNNotificationContentExtension(下文中简称 NotificationContent) 与 UNNotificationServiceExtension(下文中简称 NotificationService)的一个简要总结。

关键代码以及完整的Demo将在文末给出。

效果展示

功能简介: 一个展示图片的推送,实现点赞,评论功能,通过截取通知更改指定内容。


NotificationContentExtension

NotificationContent 可供开发者定制化通知界面的界面,开发者只需要在targets中添加 NotificationContent 的 Target即可 (创建NotificationService 时选择NotificationServiceExtension):

创建好选中的Extension后,我们看到项目生成了一个由我们命名的Target以及项目中一个文件夹(此处命名为NotificationContent),我们来详细看看文件夹里面都包含哪些内容:

NotificationViewController

可以看到其继承自UIViewController。ViewController 中能干的事情它都能干。还配套了一个Storyboard 构建界面更加的方便。
在 .m 文件中可以看到其实现了UNNotificationContentExtension协议,
进入该协议可以看到只有两个方法:

//当接收到推送需要展示时,会调用这个方法,每一条推送都会调用这个方法。
- (void)didReceiveNotification:(UNNotification *)notification;

//用于获取用户的交互事件(Notification Actions)
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion;

这里需要特别注意, 方法 didReceiveNotificationResponse 是用于获取Notification Actions的事件。
注意 :NotificationContent 面板上不支持“自定义”交互
官方只提供了一个多媒体按钮可以添加在界面上, 协议中关于该按钮的部分如下:

//媒体按钮的类型
@property (nonatomic, readonly, assign) UNNotificationContentExtensionMediaPlayPauseButtonType mediaPlayPauseButtonType;

//媒体按钮的位置、大小
@property (nonatomic, readonly, assign) CGRect mediaPlayPauseButtonFrame;

//媒体按钮的颜色
@property (nonatomic, readonly, copy) UIColor *mediaPlayPauseButtonTintColor;

//媒体按钮事件
- (void)mediaPlay;
- (void)mediaPause;

开发者只能够对位置,颜色,以及其交互事件进行简单的控制。相信苹果是为了让推送的信息面板风格整体不会太杂乱。对于视频资源,控制按钮是不得不提供的(想想一下将视频控制的交互如果放在Notification Action上的画面,其效果,交互将非常别扭)

info.plist

这里面是 NotificationContent 的一些配置信息,其中需要注意几个关键的Key:

UNNotificationExtensionCategory :用于标识当前 NotificationContent, 在接收到推送时,通过推送的 Category 参数来调用指定的 NotificationContent。默认是一个 String 类型,其也可以更改为 Array 类型,这表示一个 NotificationContent 可以代表多个 Category。

UNNotificationExtensionDefaultContentHidden:是否隐藏默认的控件。自定义通知视图下,默认的控件(title, subtitle, body)都在控件最下方展示,可通过将此 key 改为YES 来进行隐藏。

UNNotificationExtensionInitialContentSizeRatio: 视图初始化的高宽比,用于优化展示效果,具体数值依情况设置。

在调用我们的 NotificationContent 时,需要通过 配置 Category 来指定所调用的视图(与 info.plist 中UNNotificationExtensionCategory 相匹配)。配置部分如下:


NotificationServiceExtension

可以在通知展现给用户之前修改通知的内容。但是,静默推送,只播放声音或只修改推送条数的不能修改。

其文件结构如下:

我们只需要注意 NotificationService 即可,其继承自 UNNotificationServiceExtension ,在 .m 实现文件中,它重写了父类的两个方法:

// 通过调用 contentHandler 来传递修改后的推送内容
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler;

// 当在修改推送内容超时时会调用此方法
- (void)serviceExtensionTimeWillExpire;

我们通过 didReceiveNotificationRequest 方法拦截推送内容进行修改,附件的下载也在这里进行。处理的时间有限制,所以在下载资源会涉及到处理超时的问题,这时候就会会触发 serviceExtensionTimeWillExpire 方法,对修改通知进行最后的补救。不管怎样,将推送展现给用户是必须的。

想要触发推送拦截需要注意两个点:
[1] apns推送的字段中,必须包含 mutalbe-content ,值为 1
[2] 推送必须是一个展示的视图(静默推送,只播放声音、修改推送数值的不会触发)。

关于 UNNotificationAttachment

在 UNMutableNotificationContent 中,我们可以看到一个名为 attachments 的集合。其要求集合中的元素都为 UNNotificationAttachment类型。

UNNotificationAttachment 是一个媒体文件的通知,在苹果的实现流程上,是它在 ServiceExtension 中将附件信息整理打包给ContentExtension。使用 attachmentWithIdentifier:URL:options:error: 进行创建。

Identifier 是资源的标识符  
URL 是资源下载完成后缓存到 本地 的地址

对于媒体文件,支持的媒体类型以及资源大小限制如下图:

总的来说,附件方面要尽可能的在质量达标的前提下压缩其大小,避免过多的超时,异常,以达到最好的体验。


关键代码示例

在示例中推送的数据结构如下:

aps =     {
    alert =         {
        body = "XXX";                  
        title = "XXX";
    };
    badge = 1;
    category = "myNotificationCategory";
    "mutable-content" = 1;
    sound = default;
};
"image-url" = "XXX";                        //图片链接
"last-comments" = "XXX";                    //最近一条评论
iOS 10 中配置推送的代码
    UNNotificationAction * likeAction;              //喜欢
    UNNotificationAction * ingnoreAction;           //取消
    UNTextInputNotificationAction * inputAction;    //文本输入
    
    likeAction = [UNNotificationAction actionWithIdentifier:@"action_like"
                                                      title:@"点赞"
                                                    options:UNNotificationActionOptionForeground];

    inputAction = [UNTextInputNotificationAction actionWithIdentifier:@"action_input"
                                                                title:@"评论"
                                                              options:UNNotificationActionOptionForeground
                                                 textInputButtonTitle:@"发送"
                                                 textInputPlaceholder:@"说点什么"];
    
    ingnoreAction = [UNNotificationAction actionWithIdentifier:@"action_cancel"
                                                         title:@"忽略"
                                                       options:UNNotificationActionOptionForeground];
    
    //下面的Identifier 需与 NotificationContent的info.plist 文件中所配置的 UNNotificationExtensionCategory 一致,
    //本示例中为“myNotificationCategory”
    UNNotificationCategory * category;
    category = [UNNotificationCategory categoryWithIdentifier:@"myNotificationCategory"
                                                      actions:@[likeAction, inputAction, ingnoreAction]
                                            intentIdentifiers:@[]
                                                      options:UNNotificationCategoryOptionNone];
    
    NSSet * sets = [NSSet setWithObjects:category, nil];
    [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:sets];
NotificationService.m

当接收到通知,展示给用户前可在此对推送来的数据进行拦截、修改。注意超时、处理异常等问题。

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // 修改信息
    NSString * type = @"【特别关心】 ";
    self.bestAttemptContent.title = [type stringByAppendingString:self.bestAttemptContent.title];
    
    // 下载并关联附件
    NSString * urlString = self.bestAttemptContent.userInfo[@"image-url"];
    [self loadAttachmentForUrlString:urlString
                   completionHandler: ^(UNNotificationAttachment *attachment) {
                       self.bestAttemptContent.attachments = [NSArray arrayWithObjects:attachment, nil];
                       [self contentComplete];
                   }];
}



这里也将附件的处理方法贴出来供大家参考:

- (void)loadAttachmentForUrlString:(NSString *)urlString
                 completionHandler:(void (^)(UNNotificationAttachment *))completionHandler {
    __block UNNotificationAttachment *attachment = nil;
    __block NSURL *attachmentURL = [NSURL URLWithString:urlString];
    NSString *fileExt = [@"." stringByAppendingString:[urlString pathExtension]];
    
    //下载附件
    _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSURLSessionDownloadTask *task;
    task = [_session downloadTaskWithURL:attachmentURL
                       completionHandler: ^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
                          
                           if (error != nil) {
                               NSLog(@"%@", error.localizedDescription);
                           } else {
                               NSFileManager *fileManager = [NSFileManager defaultManager];
                               NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path
                                                     stringByAppendingString:fileExt]];
                               [fileManager moveItemAtURL:temporaryFileLocation
                                                    toURL:localURL
                                                   error:&error];
                               NSError *attachmentError = nil;
                               NSString * uuidString = [[NSUUID UUID] UUIDString];
                               //将附件信息进行打包
                               attachment = [UNNotificationAttachment attachmentWithIdentifier:uuidString
                                                                                           URL:localURL
                                                                                       options:nil
                                                                                         error:&attachmentError];
                               if (attachmentError) {
                                   NSLog(@"%@", attachmentError.localizedDescription);
                               }
                           }
                           
                           completionHandler(attachment);
                       }];
    [task resume];
}
NotificationContent

这里重点说一下如何提取传递过来的附件信息。

- (void)didReceiveNotification:(UNNotification *)notification {
    /*
     *    这里有一堆普通数据展示逻辑
     */

    //附件的提取,
    //这里必须注意,startAccessingXXX方法 与 stopAccessingXXX 方法是成对出现的
    UNNotificationAttachment * attachment = notification.request.content.attachments[0];
    if ([attachment.URL startAccessingSecurityScopedResource]) {
        NSData *imageData = [NSData dataWithContentsOfURL:attachment.URL];
        [self.imageView setImage:[UIImage imageWithData:imageData]];
        [attachment.URL stopAccessingSecurityScopedResource];
    }

}



didReceiveNotificationResponse 中可以处理Notification Actions的事件,这也就让推送视图上的交互效果得以成为现实,例如示例中的“评论”, “点赞”功能。

- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion {
    if ([response.actionIdentifier isEqualToString:@"action_like"]) {

        //点赞
        [self.likeLabel setHidden:!self.likeLabel.hidden];
    }else if([response.actionIdentifier isEqualToString:@"action_input"]) {

        //发送评价
        UNTextInputNotificationResponse * textResponse = (UNTextInputNotificationResponse *)response;
        [self postComment:textResponse.userText];
    } else {

        //忽略
        completion(UNNotificationContentExtensionResponseOptionDismiss);
    }
    completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
关于自定义交互

在推送的交互上,苹果也提供了自定义交互面板,完全由开发者自定义, 例如个性化的表情面板等,只需要开发者在 NotificationContent 中重写两个方法:

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (UIView *)inputView {
    return _customView;       //自定义交互视图
}

而在需要唤起自定义交互视图时,调用自定义视图的becomeFirstResponder即可,例如这里举例的customView:

[self.customView becomeFirstResponder];

笔者在测试自定义交互时,所指定的视图并不受设置的frame影响。始终从屏幕下方弹出,位置,大小始终如一,这个有待进一步测试。

详细的代码可以参看Demo
Demo地址

最后再说点什么

因为该功能需要 iOS 10 以上的系统支持,在编写代码时注意系统版本的区分。苹果再三强调推送面板上的交互要尽可能的简洁易用。

在示例的编写过程中,笔者在同时实现系统的文本输入框,以及自定义面板时,会造成输入框的 action 失效,可能是两者在响应上有冲突,等后面有空的时候再验证一下。

笔者在创建自定义视图时习惯性的直接使用了 xib ,结果编译器报错,不能导入。后来在自动生成的Storybord中创建,可以直接调用。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/17124.html

相关文章

  • iOS10 语音播报填坑详解(解决串行播报中断问题)

    摘要:在之前,苹果没有提供通知扩展类的时候,如果想要实现杀进程也可以正常播报语音消息很难,从添加了这一个通知扩展类后,实现杀进程播报语音就相对简单很多了。苹果规定,当一条通知达到后,如果在秒内,还没有呼出通知栏,我就系统强制调用来呼出通知栏。 iOS10 语音播报填坑详解(解决串行播报中断问题) 在来聊这类需求的解决方案之前,咱们还是先来聊一聊这类需求的真实使用场景:语音播报。语音播报需求运...

    psychola 评论0 收藏0
  • fir.im Weekly - 关于 iOS10 适配、开发、推送的一切

    摘要:小程序来了,微信变成名副其实的,新一轮的与争论四起。收集框架开发应用,解决问题始终是一个难题。玩转开发集成背着吉他去流浪的集成支付系列包括微信支付支付宝银联,主流的支付已经全部到位,来这里看看热更新方案来自于美团点评技术团队分享。 小程序来了,微信变成名副其实的 Web OS,新一轮的Web App 与Native App争论四起。程序员对新技术永远保持灵敏的嗅觉和旺盛的好奇心,@李锦...

    SimpleTriangle 评论0 收藏0
  • iOS小知识

    摘要:动态库与静态库在说和文件之前,先明确一下什么是静态库,什么是动态库。静态库在编译的时候会被直接拷贝一份,复制到目标程序里来点不一样的推送简介中新增的通知服务扩展功能,在推送通知展示到界面之前提供开发者可以在后台对推送的内容进行修改。 如何优雅地使用 KVO KVO 作为 iOS 中一种强大并且有效的机制,为 iOS 开发者们提供了很多的便利;我们可以使用 KVO 来检测对象属性的变化、...

    wpw 评论0 收藏0
  • iOS APNs实战分享

    摘要:对象离线下发个推的透传消息,发送推送消息。更换证书这种方案是在发布时重新上传生产证书,注意个推的证书更换后需要分钟左右生效,这种方案需要注意在之后版本更新开发时,需要申请新的个推,不然会影响在线的客户。 showImg(https://segmentfault.com/img/bV8riL?w=800&h=444); 序言: 因为App的功能需要,最近一直在调研苹果的APNs推送,开始...

    BigTomato 评论0 收藏0
  • 想要更精准的小程序模版消息推送?我们来帮你实现

    摘要:在用户喜爱的众多功能中,使用率最高的是模版消息推送。模版消息推送数的量级也由早期每天几百条,变为后来的每天数百万条。平台支持少知晓云已经支持包括微信小程序和支付宝小程序在内的各大小程序平台的消息推送,对平台的支持也将在近期上线。 两年多前,为了让更多的人找到好玩、好用的小程序,我们成立了「知晓程序」。 再后来,我们推出了后端云服务平台——知晓云,帮助大家降低创业成本,提升开发效率。 「...

    RobinTang 评论0 收藏0

发表评论

0条评论

huhud

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<