iOS极光推送带图片模式(Service Extension)
以下所有内容均为个人观点,转载请注明出处<简书--小蜗牛吱呀之悠悠 >,谢谢!
最近工作中新增一个告警推送带图片的需求,要求在云端服务器向极光推送图片地址,APP在收到推送消息以后能够将地址对应的图片下载完成并显示在推送栏右侧,经过一段时间对极光推送官方文档和iOS10新特性Service Extension的研究,终于实现了该功能的开发。由于是初次使用此功能,遇到不少问题,为做记录以及方便大家查阅,故写下这篇博客,若有不恰当之处敬请留言指导,不胜感激,话不多说,开始正文!
一、极光推送的流程介绍
通常,后台推送比较常见,本文仅介绍关于后台极光推送的实现流程。如下图红色箭头所示
image.png
1、云服务器调用极光的接口向极光发送消息,极光收到消息后会将消息发送给苹果的APNs服务器,苹果再根据唯一标示找到手机,手机收到推送消息后,会在屏幕上方弹出推送消息。
2、iOS 10以前,iOS的推送只能实现推送文字消息,这时候就需要云服务器将推送的内容发送给极光,APP展示在界面上。iOS 10以后,苹果官方新增了一些特性,其中包括新的推送方式----推送拦截(Service Extension),这种推送方式允许iOS开发者对接收到的推送消息进行一定的处理,以便达到自己想要的效果。
2、Service Extension介绍
image.png如上图所示,在iOS 10以后,在APNs与APP之间增加了一个扩展,这意味着在APNs到达我们的设备之前,还会经过一层允许用户自主设置的Extension服务进行处理,为APNs增加了多样性,在这个Extension中,我们可以图片、音频(比如支付宝的到账提醒语音),我们甚至可以修改显示的文字消息(意义不大,如果需要修改,直接让服务器去修改推送的内容就可以了)。
3、Service Extension集成介绍
极光推送的内容此处忽略,需要学习的请参考https://docs.jiguang.cn/jpush/client/iOS/ios_guide_new/
1、新建Service Extension,File->New->Target
image.png
需要注意的一点是,这是一个新的target,bundle ID与主APP不同,它是主APP的一个扩展,必须是主APP的bundle ID拼上target的名字。
创建完成后,会多一个target,如下图:
image.png
2、此时主工程需要如下配置:
image.png
3、对Extension做如下配置:
image.png
4、将极光的库导入工程并引入依赖库: image.png
5、接下来就需要为新的target制作证书了,从APP IDs 到 Provisioning Profiles, 配置好开发环境、生产环境的Provisioning Profile之后,依次选择对应的证书即可,关于如何制作证书,这里不赘述,请出门右拐找度娘。
证书配置完毕后,APP的准备工作算是完成了,服务器需要将字段:"mutable -content" 设置为true后再发给极光,具体的集成步骤,请服务器人员自行参考极光官网。
二、Service Extension target编码
进入到Xcode自动给你创建的.m文件中,会发现有2个方法。
-
didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler
-
serviceExtensionTimeWillExpire
第一个方法就是我们用于拦截推送消息后,自定义消息内容的方法。第二个方法是对第一个方法的补救。第二个方法会在过期之前进行回调,此时你可以对你的APNs消息进行一下紧急处理,比如多媒体文件过大,下载的时间过长,会直接调用此方法。
注意:媒体资源文件不宜过大,否则耗时过长,无法达到拦截效果
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
/*
* 解析图片的地址
* 收到的字典数据结构并不是和服务器约定好的,为了通用性,此处递归解析字典
*/
NSString *imgUrl = [self getURLForUserInfo:self.bestAttemptContent.userInfo];
if (imgUrl) {
//download
NSURL *fileURL = [NSURL URLWithString:imgUrl];
[self downloadAndSave:fileURL handler:^(NSString *localPath) {
if (localPath) {
UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:@"myAttachment" URL:[NSURL fileURLWithPath:localPath] options:nil error:nil];
self.bestAttemptContent.attachments = @[attachment];
}
//此方法为极光SDK中的用于上传appKey的方法
[self apnsDeliverWith:request];
}];
}else{
[self apnsDeliverWith:request];
}
}
此处收到的字典,但字典的数据结构并不是与云服务器约定好的结构,此问题咨询过极光的工作人员,并未给出合理的解释,此处为了规避数据结构异常导致的问题,采用递归解析的方式获取地址。解析方法如下:
- (NSString *)getURLForUserInfo:(NSDictionary *)userInfo {
__block NSString *url = nil;
__block __weak __typeof(&*self)weakSelf = self;
[userInfo enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([key containsString:@"url"]) {
*stop = YES;
url = obj;
}
if ([obj isKindOfClass:[NSDictionary class]] || [obj isKindOfClass:[NSArray class]]) {
url = [weakSelf getURLForUserInfo:obj];
}
}];
return url;
}
由于这是扩展的target,无法使用主工程的网络请求库,需要使用原生的网络请求方法下载资源文件:
- (void)downloadAndSave:(NSURL *)fileURL handler:(void (^)(NSString *))handler {
NSURLSession * session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:fileURL completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString *localPath = nil;
if (!error) {
//临时文件夹路径,APP没有运行时会自动清除图片,不会占用内存
NSString * localURL = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(),fileURL.lastPathComponent];
if ([[NSFileManager defaultManager] moveItemAtPath:location.path toPath:localURL error:nil]) {
localPath = localURL;
}
}
handler(localPath);
}];
[task resume];
}
资源文件下载失败,紧急处理:
- (void)serviceExtensionTimeWillExpire {
self.contentHandler(self.bestAttemptContent);
}
三、Service Extension target调试
编码完成后,通常需要调试,主要涉及两个方面,一个是扩展工程的在线调试,另一个是极光推送的编译环境调试。
1、极光在线调试
进入极光主页,进入如下界面:
image.png
配置相关信息,需要注意的一点是,Service Extension target需要勾选下图中的选项:
image.png
点击推送后,APP即可收到后台推送消息。如果需要显示图片,则在附件字段一栏,设置好键值对。
2、扩展工程在线调试
首先需要运行主工程文件,等主工程文件运行起来后点击下图操作,过一段时间会出现你的扩展工程(有时候会出不来,感觉像是Xcode的bug),当你接受的推送后,会在你的扩展工程中的断点处停下。
扩展工程调试.png
3、字段调试
如果2中所提的调试方法无法使用断点,可以采用修改推送内容的方式来调试,如下:
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [NotificationService]", self.bestAttemptContent.title];
此时,你收到的推送消息将会拼接上[NotificationService],如果是这种格式显示的,那么恭喜你,你的扩展拦截是OK的;如果收到的没有拼接上,那么,说明你拦截失败,需要考虑你的集成步骤是否正确,或者是证书的生成是否OK。