看之前如果你对iOS10 的推送还处于一片空白,建议先看
iOS 10 推送你玩过了吗?
Notification Extension
iOS10 添加了很多的Extension,与通知相关的 extension 有两个:Service Extension 和 Content Extension。
我们先来了解一下Service Extension,这个东西主要是干啥的呢?
主要是,让我们在收到远程推送的时候<注意哈,必须是远程推送>,展示之前对通知进行修改,因为我们收到远程推送之前会先去执行Service Extension中的代码。这样就可以在收到远程推送展示之前为所欲为了。
用一张图来表示下这种流程:
Service Extension能干啥?
举个栗子:
<1>、通过远程推送,推送的内容的
title="1"
,我可以在收到推送将要显示之前将标题修改成title="2"
,那么推送条展示的title就是2。<2>、第一点也就是举个栗子,一般不会有这种需求,既然能修改内容,那么可以在发送推送的时候发送一段用公钥加密的内容,然后设备收到推送之后,用私钥进行解密然后再去展示。有啥好处呢,这么折腾。有很多的推送用的是三方的<极光推送,友盟推送,百度推送等等>。这样你推送的东西,就不会让三方看到了。如果是金融之类的,要更注意保密吧。
<3>、当远程推送需要展示多媒体的时候,也需要在这下载,下载完毕之后,获取本地下载文件路径再进行展示。对下载附件多媒体得需要Service Extension这玩意儿。
介绍完毕,开始玩一下。
1、创建一个Service Extension
Paste_Image.png
Paste_Image.png
Paste_Image.png
创建完毕之后看一下项目的变化
Paste_Image.png Paste_Image.png
Paste_Image.png
大概这几个比较明显的地方
创建完毕,看一下里面的相关的方法
方法override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)
大概理解下:
收到一个远程推送的时候就会调用这个方法,你有30秒的时间来修改接收到的推送的内容<你可以去修改通知的内容,你也可以去下载推送的附件>,如果超过30秒了,你没有做任何的处理,系统就会自动的调用
override func serviceExtensionTimeWillExpire()
,如果你还不做出相应的操作,那么将显示最开始的通知的内容。
怎么进行测试呢?
第一步,选择要测试的target,然后 run
第二步,选择自己的app
Paste_Image.png
第三步,来个小小的断点
Paste_Image.png
第四步,发个远程推送,本地推送自己可以尝试,是没反应的。
2、玩一把
{
"aps":{
"alert":{
"title":"iOS 10 title",
"subtitle":"iOS 10 subtitle",
"body":"iOS 10 body"
},
"category":"saySomethingCategory",
"sound":"default",
"badge":3
}
}
推送收到了,但是没有走断点。
原因是推送的内容中缺少一个字段
mutable-content 表示我们会在接收到通知时对内容进行更改。
{
"aps":{
"alert":{
"title":"iOS 10 title",
"subtitle":"iOS 10 subtitle",
"body":"iOS 10 body"
},
"mutable-content":1,
"category":"saySomethingCategory",
"sound":"default",
"badge":3
}
}
添加上之后再玩一把,在展示推送之前进行走断点了。
放过断点,我们发现标题已经被更改了。
Paste_Image.png
Paste_Image.png
3、尝试更改其他的内容
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
bestAttemptContent.body = "我是新修改的body"
bestAttemptContent.title = "我是新修改的title"
bestAttemptContent.subtitle = "我是subTitle"
contentHandler(bestAttemptContent)
}
}
Paste_Image.png
更改推送内容,有没有这种需求呢?
我感觉有,比如你是一家金融公司,你给用户单独推送的东西不想让别人抓包或者其他的方式看到,那么你可以在推送的时候进行加密,然后到了客户端之后再进行解密。这样做的话就比较安全一些。
4、展示远程推送过来的图片
先说一下思路,收到推送之后,判断有相关附件的话就去下载,下载完毕存储到沙盒中,然后显示。即时是过了30秒如果图片没下载下来,也会显示最开始的推送的内容的。
主要代码:
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
bestAttemptContent.body = "我是新修改的body"
bestAttemptContent.title = "我是新修改的title"
bestAttemptContent.subtitle = "我是subTitle"
// 获取相关的附件,看是不是有相关的附件
let userInfoDic = bestAttemptContent.userInfo
let apsDic:[AnyHashable : Any] = userInfoDic["aps"] as! [AnyHashable : Any]
if apsDic["my-attachment"] != nil{
// 如果说存在附件的话
loadAttachmentForUrlString(urlStr: apsDic["my-attachment"] as! String, completionHandler: { (attachment) in
if attachment != nil{
// 如果说 attachment 不为空的话
bestAttemptContent.attachments = [attachment!,attachment!]
}
contentHandler(bestAttemptContent)
})
}else{
// 如果说不存在附件的话
contentHandler(bestAttemptContent)
}
}
}
下载方法,下载完毕之后通过block进行回调
// MARK:- 下载相关的附件信息
func loadAttachmentForUrlString(urlStr:String,completionHandler:@escaping CompletionHandlerBlock) -> Void {
let attachmentURL:URL = URL(string: urlStr)!
// 开启线程去下载
DispatchQueue.global().async {
let urlSession = URLSession.shared.dataTask(with: attachmentURL, completionHandler: { (data, response, error) in
// 下载完毕,或者报错,转到主线程中
DispatchQueue.main.async {
var attachment : UNNotificationAttachment? = nil
if let data = data{
// 加工目的 url,将下载好的推送附件添加到沙盒中
let ext = (urlStr as NSString).pathExtension
let cacheURL = URL(fileURLWithPath: FileManager.default.cachesDirectory)
let finalUrl = cacheURL.appendingPathComponent(urlStr.md5).appendingPathExtension(ext)
print("finalUrl ************* \(finalUrl)")
// 将下载好的附件写入沙盒
if let _ = try? data.write(to: finalUrl) {
// 写入
attachment = try? UNNotificationAttachment(identifier: "pushAttachment", url: finalUrl, options: nil)
completionHandler(attachment)
}else{
completionHandler(attachment)
}
}else{
completionHandler(attachment)
}
}
})
urlSession.resume()
}
}
这里没有去写用SDWebImageView下载,也没有用AFNetworking等三方库去下载,我也想啊,但是报错。报错信息如下
Paste_Image.png
有知道怎么解决的,跪求。
推送如下,收到推送去下载,但是说下载失败,报错原因
MyServiceExtension[12788:2223445] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
解决方案:
Paste_Image.png
终于的终于,实现效果:
当然了对于这种推送附件缓存,建议每次推送的时候清理一下,不然越来越大了。并且还发现在不同的target下的沙盒地址是不一样的
Paste_Image.png
我感觉要是清理的话,可以在每次收到远程推送的时候清理一下。
远程推送视频,音乐的,我木有相关的url资源,木有尝试,不过应该差不多。
有一些小伙伴问,为什么配置完毕之后回报错,真机运行的时候也报错
类似这样的
myServiceExtension has conflicting provisioning settings.
myServiceExtension is automatically signed,
but code signing identity iPhone Developer: xxxxxxx has been manually specified. Set the code signing identity value to "iPhone Developer"
in the build settings editor, or switch to manual signing in the project editor.’
解决的办法参考如下:
1、选择 Automaticlly manage signing
选择相关的team
Paste_Image.png
2、signing的相关设置
都设置成Developer,这个真机调试,打包测试,上线这里都可以不用修改。Xcode会根据你的操作自动配置相关的证书和Profile文件,前提是你的相关环境下的证书和Profile文件都是齐全的<这个应该不是问题,不然你选择当前环境下的包是怎么打出来的>
最后,献上参考Demo地址:https://github.com/RunOfTheSnail/PushDemo001
参考资料:
http://www.cocoachina.com/ios/20160628/16833.html
https://onevcat.com/2016/08/notification/
http://www.cnblogs.com/lidongq/p/5968923.html
https://developer.apple.com/reference/usernotifications/unnotificationattachment
图画的不错
http://www.jianshu.com/p/2f3202b5e758
http://www.cocoachina.com/ios/20161021/17820.html
网友评论
我选择的推送证书,是我app的推送证书,不是MyServiceExtension中BundleId对应的证书,这个有影响吗?
参考下另一篇文章
https://www.jianshu.com/p/5fc3b4155257
但是我又遇到一个问题,我语音播放的内容比较长,播放不完,但是我看你的demo是可以完全播放完的,我想问一下,你怎么处理的?
我发现的现象是,我这边,推送成功后,推送的消息框就出现了,但是我看你demo是在播放后之后才出现推送的提示框的?
这里下面我写了相关的调试方法,你看下对你有帮助吗
我这边重新试了一下,都能听到声音,但是我记得刚开始做的时候,视频播放没有声音,也可能记错了,或者Demo演示的视频的声音比较小。
现在实验的结果就是,声音的确不大,但是能够听清楚。视频和音频的声音都不大。我想是苹果那边就这么处理的,因为你的推送的目的说白了,就是概览一下相关的内容而已。想要看相关的东西的话还是引导用户进入app中去查看吧,个人理解。
现在我还没有解决办法,如果你有的话,欢迎交流。
myServiceExtension is automatically signed, but code signing identity iPhone Developer: xxxxxxx has been manually specified. Set the code signing identity value to "iPhone Developer" in the build settings editor, or switch to manual signing in the project editor.’
然后用真机运行又报错
myServiceExtension has conflicting provisioning settings. myServiceExtension is automatically signed, but code signing identity iPhone Developer: xxxxxx has been manually specified. Set the code signing identity value to "iPhone Developer" in the build settings editor, or switch to manual signing in the project editor.
Code signing is required for product type 'App Extension' in SDK 'iOS 10.3'
请问这个需要对之前的证书做什么处理吗
http://www.jianshu.com/p/00f671d204d4
这里面提到了跨不同的extension访问沙盒资源了,不知道你说的是不是这种情况?
这里面有详细的介绍。