更新晚啦,我要先跟大家说声抱歉哈~
如果大家还没有看我的这两篇文章,建议还是先阅读一下,循序渐进么~文章链接如下:
iOS开发 iOS10推送必看(基础篇)
iOS开发 iOS10推送必看(高阶1)
这次的最后,终于有demo咯~
在这篇文章,我会给大家讲一讲更高级一点的,定制化更高的远程通知。其中会补充我之前没讲的远程推送(多媒体)通知,以及UNNotificationServiceExtension,UNNotificationContentExtension,UNNotificationAction的相关类。相信大家看了这篇文章,虽不能说对苹果的远程推送了如指掌,但是也可以做一些基本的扩展咯~
1、UNNotificationServiceExtension
1.1、UNNotificationServiceExtension简介
UNNotificationServiceExtension是iOS10推出后的一个新特性,先看这张图:
Extension01.png-50.1kB
从这张图上,我们可以看到,原先直接从APNs推送到用户手机的消息,中间添加了ServiceExtension处理的一个步骤(当然你也可以不处理),通过这个ServiceExtension,我们可以把即将给用户展示的通知内容,做各种自定义的处理,最终,给用户呈现一个更为丰富的通知,当然,如果网络不好,或者附件过大,可能在给定的时间内,我们没能将通知重新编辑,那么,则会显示发过来的原版通知内容。
那么ServiceExtension可以做什么呢?它的意义是什么呢?我总结了几点:
-
1、 安全
安全总是摆在第一位的,从iOS9开始,苹果鼓励我们使用更为安全的https协议可以看的出来,苹果公司是对安全很重视的一家公司,为什么在这里我会提到安全呢?是因为之前我们的推送内容,不管是通过第三方,还是通过苹果自带的通知处理,如果让有心人对数据做一次拦截,抓个包啥的,我们推送的内容就会完全暴露,当然有的同学说,我可以加密啊~但是不知道大家有没有想过,如果数据加密,那通知栏会怎么展示呢?(你千万别跟我说你把所有的远程推送变成本地通知。。)通过此次这次增加的UNNotificationServiceExtension的类,便可以更好的帮助我们实现数据的加密。
它的原理便是在收到通知后的最多30s内,你可以把你的通知内容,解密后,在重新展示在用户的通知拦上。 -
2、 内容的丰富
之前的通知展示内容比较少,以至于被各种广告提醒占据了。这次苹果新添加的附件通知,结合上通知拓展类,便可以给用户展现出一个有着丰富内容的通知。比如,一个小短片的某一秒的画面啊(这里要强烈鄙视各大平台的某些电影预览图),又或者是配上一些小图片啊(通过服务器传来的imaUrl)等方式来吸引用户,诱导用户点开你的通知,促使用户会使用你的App。其实推送这个功能,虽然有的人会关闭,但是大部分的人还是开启的,所以说推送这个市场还是很大的哟~灵活利用推送,会让你的程序拥有无限的可能。
1.2、如何新建一个UNNotificationServiceExtension
首先,我们不能通过创建UNNotificationServiceExtension的类来使用服务扩展,我们应当创建一个Target,这个Target自带一个模板,其中有2个方法是系统会自己调用的,如下:
// 你需要通过重写这个方法,来重写你的通知内容,也可以在这里下载附件内容
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler;
// 如果处理时间太长,等不及处理了,就会把收到的apns直接展示出来
- (void)serviceExtensionTimeWillExpire;
开始跟着我创建一个UNNotificationServiceExtension吧。
新建Target
QQ20160927-3.png-160.3kB选择如图所示:
QQ20160927-4.png-115.2kB
然后写名字,下一步,即可
此时我们的目录结构里面,已经多出了一个文件夹了。
QQ20160927-5.png-11.5kB
QQ20160927-6.png-11.7kB
都多了一个myTest。
22172ED.png-33kB
注意看上图,这里的bundleID是你的工程名字的bundleID加上.名称。
不要修改,系统创建的时候就创建好了,不过我还是给大家说一下这个格式
如果你的工程的BundleID是
coderxu.pushDemo
,则这个扩展的BundleID就是coderxu.pushDemo.mytest
,最后的后缀,是看咱们创建服务扩展时候的名字。其他的小细节,大家可以看看。到这一步,我们就新建了一个服务通知类的扩展。
1.3、如何使用以及相关Demo
在使用这个类的时候,我重写了以下代码,大家可以先看下:
(1). 这是处理通知内容重写的方法:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
// copy发来的通知,开始做一些处理
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
// 重写一些东西
self.bestAttemptContent.title = @"我是标题";
self.bestAttemptContent.subtitle = @"我是子标题";
self.bestAttemptContent.body = @"来自徐不同";
// 附件
NSDictionary *dict = self.bestAttemptContent.userInfo;
NSDictionary *notiDict = dict[@"aps"];
NSString *imgUrl = [NSString stringWithFormat:@"%@",notiDict[@"imageAbsoluteString"]];
if (!imgUrl.length) {
self.contentHandler(self.bestAttemptContent);
}
[self loadAttachmentForUrlString:imgUrl withType:@"png" completionHandle:^(UNNotificationAttachment *attach) {
if (attach) {
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];
}
self.contentHandler(self.bestAttemptContent);
}];
}
(2). 这是下载附件通知的方法:
- (void)loadAttachmentForUrlString:(NSString *)urlStr
withType:(NSString *)type
completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler{
__block UNNotificationAttachment *attachment = nil;
NSURL *attachmentURL = [NSURL URLWithString:urlStr];
NSString *fileExt = [self fileExtensionForMediaType: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]];
[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];
}
(3)判断文件类型的方法
- (NSString *)fileExtensionForMediaType:(NSString *)type {
NSString *ext = type;
if ([type isEqualToString:@"image"]) {
ext = @"jpg";
}
if ([type isEqualToString:@"video"]) {
ext = @"mp4";
}
if ([type isEqualToString:@"audio"]) {
ext = @"mp3";
}
return [@"." stringByAppendingString:ext];
}
第一段代码主要讲通知内容的重组,逻辑就是有附件的url,我就下载,如果没有url我就直接展示通知。
第二段代码主要讲的是,用系统自带类,下载图,存图,找到filePath,创建通知的附件内容。(创建附件的url,必须是一个文件路径,也就是说,必须下载下,才能获取文件路径,开头是file://)
第三段的代码主要讲判断文件的后缀类型,然后前端好处理。这里我的代码是写死了,因为我就测试一张图。最好的方式是服务器返回的 推送内容中,带有附件的类型。我的iOS开发 iOS10推送必看(高阶1)一文中,有讲多媒体附件的类型,以及相关的大小限制。
这里强烈建议,处理推送内容的时候,让服务器带上文件类型。重要的事情说三遍
这里强烈建议,处理推送内容的时候,让服务器带上文件类型。重要的事情说三遍
这里强烈建议,处理推送内容的时候,让服务器带上文件类型。重要的事情说三遍
在补充一些问题
-
1.在这个推送服务的扩展类中,为什么我使用了系统自带类下载,没有使用AFN?
答:不知道为什么,导入AFN后总是出现各种编译报错啊。主要涉及这几个情况
1.用cocoapods导入AFN,在类引入AFN,编译报错!!
2.手动拖拽AFN进工程(不勾选),在类引入AFN,编译报错!!
3.手动拖拽AFN进工程(勾选),直接编译报错!!
4.下载用通知回调的方式(慢,线程不确定)
最后我就选择这个block回调,系统类下载的方式,来下载通知中的附件。
以下是一些错误的截图:(可以不看)
QQ20160927-0.png-100.9kB
QQ20160927-1.png-21.6kB
QQ20160927-2.png-228.5kB
-
2.如果调试这个通知扩展类,为什么我跑程序的时候,打断点无反应?
答:这是因为你跑的target不对,正确的做法是,跑正确的target,具体如下图:
QQ20160927-7.png-32.8kB
选择你的程序Target
QQ20160927-8.png-49kB这个时候,大家在打断点,就可以啦~
最后,在附上推送的内容格式。
推送内容格式如下:
{
"aps": {
"alert": "This is some fancy message.",
"badge": 1,
"sound": "default",
"mutable-content": "1",
"imageAbsoluteString": "http://upload.univs.cn/2012/0104/1325645511371.jpg"
}
}
这里我们要注意一定要有"mutable-content": "1"
,以及一定要有Alert的字段,否则可能会拦截通知失败。(苹果文档说的)。除此之外,我们还可以添加自定义字段,比如,图片地址,图片类型,大家慢慢摸索下吧有问题可以留言哟
这一章的最后,附上成功推送的展示图:
QQ20160927-9.png-383.5kB
稍后补充以下内容~
# 2、UNNotificationContentExtension
## 2.1、UNNotificationContentExtension简介
## 2.2、如何新建一个UNNotificationContentExtension
## 2.3、如何使用以及相关Demo
# 3、UNNotificationAction
## 3.1、UNNotificationAction简介
## 3.2、如何新建一个UNNotificationAction
## 3.3、如何使用以及相关Demo
如果你喜欢我的文章,不要忘记关注我,谢谢大家了~
另外如果你要转载,希望可以注明出处,我会写出更多更好的文章,来回馈大家~
网友评论
// 创建语音合成器
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:self.bestAttemptContent.body];
utterance.rate = 0.53;// 语速
utterance.pitchMultiplier = 1.1;//音调 0.5(底音调)~2.0(高音调)之间
utterance.volume = 1.0;//声音大小, 0.0 ~ 1.0 之间
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];
utterance.rate = AVSpeechUtteranceDefaultSpeechRate;
[synthesizer speakUtterance:utterance];
alert = 12312312;
badge = 1;
"content-available" = 1;
"mutable-content" = 1;
sound = "push.wav";
};
alert = "This is some fancy message.";
badge = 1;
imageAbsoluteString = "http://upload.univs.cn/2012/0104/1325645511371.jpg";;
"mutable-content" = 1;
sound = default;
};
}
但是不显示图片,什么原因,用的你demo运行的
1、NotificationService has conflicting provisioning settings. NotificationService is automatically signed, but provisioning profile 8775c99b-8330-4c3f-912f-a0f2e151338b has been manually specified. Set the provisioning profile value to "Automatic" in the build settings editor, or switch to manual signing in the project editor.
2、Code signing is required for product type 'App Extension' in SDK 'iOS 10.3'
什么原因 ,content的就不报错
APP被杀死的情况下接收到了推送 并播报了 alert里面的内容
在兼容iOS 10之前的系统时 和后台规定了 sound 的声音
出现了同时播放sound并播报了 alert里面的内容的情况
Provisioning profile "Development H0716" doesn't match the entitlements file's value for the application-identifier entitlement.
Code signing is required for product type 'App Extension' in SDK 'iOS 10.3'
为么编译一直报这个错?boundle ID需要怎么整?麻烦请问一下
self.bestAttemptContent.title = @"我是标题";
self.bestAttemptContent.subtitle = @"我是子标题";
self.bestAttemptContent.body = @"我是body";
这些方法并不执行,通知显示的依旧是我原始填写的内容,仿佛没有添加NotificationService一样,请问你觉得会是哪里出了问题呢?
{
"aps": {
"alert": "This is some fancy message.",
"badge": 1,
"sound": "default",
"mutable-content": "1",
"imageAbsoluteString": "http://upload.univs.cn/2012/0104/1325645511371.jpg"
}
}
图片不显示。只是修改了demo的bundle identifier为我自己的bundle identifier,其他没有修改
"aps":{ "alert":{ "title":"I am title", "subtitle":"I am subtitle", "body":"I am body" }, "sound":"default", "badge":1 } }
在iOS7.0 iOS8.1.3上同样也接收到了通知,请问是苹果做了兼容性调整了吗? 新版的payload现在能向前兼容到哪个版本呢?
Service Extension的Bundle Identifier需要在Main Target的命名空间下,比如说Main Target的BundleID为io.jpush.xxx,那么Service Extension的BundleID应该类似与io.jpush.xxx.yyy这样的格式。如果不这么做,你可能会遇到一个错误。
意思是说我在开发者中心注册了一个 io.jpush.xxx的id,我还需要注册一个io.jpush.xxx.yyy的id才行么,就是说其实项目里面用到了两个id么,谢谢。
self.contentHandler = contentHandler;方法的对吧??
error: Embedded binary is not signed with the same certificate as the parent app. Verify the embedded binary target's code sign settings match the parent app's.
Embedded Binary Signing Certificate: - (Ad Hoc Code Signed)
Parent App Signing Certificate: iPhone Developer:
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 收到远程通知:" );
// 更新显示的徽章个数
NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber;
badge--;
badge = badge >= 0 ? badge : 0;
[UIApplication sharedApplication].applicationIconBadgeNumber = badge;
} 但是我这样并不能实现累加, 还是说只能用
NSNumber *badge = content.badge; // 推送消息的角标
[UIApplication sharedApplication].applicationIconBadgeNumber = badge;
这样的方式?
badge--;
badge = badge >= 0 ? badge : 0;
[UIApplication sharedApplication].applicationIconBadgeNumber = badge;这个方法没有角标显示、没有角标显示、没有角标显示。
但是用content.badge的时候如果后端传的这个参数的话会有角标显示。
但是我不明白是为什么?希望楼主给一个思路~