四、<iOS 远程推送> iOS 10 UNNoti
之前一直在想苹果的 iOS10 远程推送能不能做得像其本地推送一模一样的效果,那样炫那样酷?答案是一定的。让我们欣赏一下 iOS10 本地推送。

远程如何实现这些功能, iOS 10 提供 Notification Service Extension,就是用它了。那么 Extension 是如何用?如果用 Extension 时,该应用是否能安装在低于 iOS 10的手机上?Extension 是否能下载网上的资源?如果能,能下载多大的东东?能下载多长时间?为什么 iOS 10 推送那么酷,几乎没有一款应用使用它呢?
带着一列的问题,对 iOS 10 远程推送进行深入的研究,对于研究新的东西,我一般习惯是看它能做什么?能做成什么样子,自己再尝试着去实现它,完善它。
一、如何创建 推送 Extension
- 1、在 Xcode -> File -> Target

-
2、选取 Notification
-
3、给 Extension 文件命名,并点击完成
Snip20170728_51.png
-
4、给 Extension 文件的 Bundle ID 是 工程名字的 Bundle ID 加上.名称
思考:若应用程序安装在手机上, Extension 会不会也在手机界面上显示 Extension 图标?
答案:Extension 不会也在手机界面上显示 Extension 图标,验证过。
二、UNNotificationServiceExtension API 的研读
在生成 Extension 后,系统会自动生成两个文件:NotificationService.h 和 NotificationService.m 。 NotificationService 是继续 UNNotificationServiceExtension,那么UNNotificationServiceExtension 是什么的干活??

如下图所示 APNs 是将消息首先推送给 ServiceExtension ,ServiceExtension 会处理好数据后再展示给用户一个优美的界面,ServiceExtension 就是一个厨师,收到原材料,给用户做成美味的食物一样,so sweat!

在展示用户推送内容前,UNNotificationServiceExtension 是修改远程推送携带的内容。UNNotificationServiceExtension 类 可以让开发者自定义推送展示的内容,Notification Service app extension 不会自己提供推送界面的,当将适当类型的推送传递给用户的设备时,它会按需启动。你可以用 extension 修改推送内容和下载推送相关的资源。你可以在extension 中解密和加密的数据或下载推送相关的图片。
你不需要自己创建 UNNotificationServiceExtension 实例,Xcode 已经帮我们创建好了。当满足如下两个条件,应用收到远程推送时,应用会加载 extension 和调用 didReceiveNotificationRequest:withContentHandler:
方法。
-
远程通知的配置是展示 Alert
The remote notification is configured to display an alert. -
远程推送的 aps 字典中,mutable-content : 1
The remote notification’s aps dictionary includes the mutable-content key with the value set to 1.
注意:不能修改静默推送或推送内容是声音或者应用的角标
在 didReceiveNotificationRequest:withContentHandler: 里面可以处理远程推送内容,修改远程推送内容的时间是有限的。如果修改内容任务没有完成,系统会调用 serviceExtensionTimeWillExpire
方法,给你提供最后一次提供修改内容的机会。如果你没有修改远程推送成功,系统将会展示远程推送最原始的内容。
三、修改远程推送内容代码实现
请欣赏下图 gif 动画,接受远程推送并下载图片。

关于如何注册通知和配置证书,请参考 三、<iOS 远程推送> 静默推送 ,其代码都是相同的,只是发送推送时内容不一样。
一、代码
在 NotificationService.m 中
#import "NotificationService.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
//推送内容
NSDictionary *dict = self.bestAttemptContent.userInfo;
NSDictionary *noticeDict = dict[@"aps"];
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@",noticeDict[@"title"]];
self.bestAttemptContent.subtitle = [NSString stringWithFormat:@"%@",noticeDict[@"subtittle"]];
self.bestAttemptContent.body = [NSString stringWithFormat:@"%@",noticeDict[@"boby"]];
self.bestAttemptContent.categoryIdentifier = @"categoryIdentifier1";
//取出文件类型
NSString *fileType = [NSString stringWithFormat:@"%@",noticeDict[@"fileType"]];
NSString *imgUrl = [NSString stringWithFormat:@"%@",noticeDict[@"imageAbsoluteString"]];
NSLog(@"%@",imgUrl);
if (!imgUrl.length) {
self.contentHandler(self.bestAttemptContent);
}
NSLog(@"%@",imgUrl);
[self loadAttachmentForUrlString:imgUrl withType:fileType completionHandle:^(UNNotificationAttachment *attach) {
if (attach) {
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];
}
self.contentHandler(self.bestAttemptContent);
}];
}
//此方法调时,是修改远程推送失败
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
self.contentHandler(self.bestAttemptContent);
}
#pragma mark - 私有方法
//下载附件 并发送
- (void)loadAttachmentForUrlString:(NSString *)urlStr
withType:(NSString *)type
completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler{
__block UNNotificationAttachment *attachment = nil;
NSURL *attachmentURL = [NSURL URLWithString:urlStr];
NSString *fileExt = [@"." stringByAppendingString:type];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[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]];
NSLog(@"%@",localURL);
[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
NSError *attachmentError = nil;
attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];
if (attachmentError) {
NSLog(@"%@", attachmentError.localizedDescription);
}
}
completionHandler(attachment);
}] resume];
}
@end
二、info.plist 配置
另外需要配置 info ,不然会报名如下图的错误

两个文件中的 info.plist ,建议都要配置一下。

配置的内容如下
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
三、推送的内容
提供一个强大的基于 MAC 的推送服务器,SmartPush。运行 Xcode 会出现如下图的弹框。

{
"aps": {
"alert": "This is some fancy message.",
"badge": 1,
"sound": "default",
"mutable-content": "1",
"imageAbsoluteString": "http://upload.univs.cn/2012/0104/1325645511371.jpg",
"title" :"hello world",
"subtittle":"tips",
"boby":"may maker help us all",
"fileType":"jpg"
}
}
四、注意点
- 1、推送前一定要记得注册。
- 2、推送的 "aps"字典中,一定要包含 mutable-content 键,并且 mutable-content 的值为 1。
- 3、测试时,运行是在下图 GDNotifications 时,NotificationService.m 中的 log 不会打印出来,请将运行切换成 Remote,就 OK!

五、总结
- 1、如果用 Extension 时,该应用是不能安装在低于 iOS 10的手机上,本人亲自测试过。因为 Extension 只能用于 iOS10 以上版本,因此市场很少有那么叼的远程推送。
- 2、Extension 能够在下载网上资源,但是下载的时间是有限的,本人尝试下载 gif 图,没有成功,如果你想加载 视频或者动图,建议下相关的资源放在本的中。下图是远程推送加载本地资源。

- 3、播放视频时,第一次可能会加载失败。
