通知相关系列文章
iOS10 之前通知使用介绍
[iOS] 通知详解: UIUserNotification
iOS10 相关API
[iOS] 通知详解:iOS 10 UserNotifications API
iOS10 本地/远程通知
[iOS] 通知详解: iOS 10 UserNotifications
iOS10 通知附加包
[iOS] 通知详解: iOS 10 UserNotifications – 附加包Media Attachments
iOS10 自定义UI
[iOS] 通知详解: iOS 10 UserNotifications – 自定义通知UI
无论是远程通知还是本地通知,都可以添加附加包,自己根据文件URL来创建UNNotificationAttachment实例,然后添加到相应的通知请求的UNMutableNotificationContent实例中。区别是获取附件的方式,一般本地通知的附件是放在本地的Bundle中的,只需要在创建本地通知的时候,根据附件的URL创建相应的UNNotificationAttachment即可;远程通知,需要根据远程的通知携带的URL地址,去初始化UNNotificationAttachment,接着就会通过Service Extensions服务来下载这些附件数据,在通知中进行显示。
上面说了,本地通知的附加包数据是放在本地的,直接根据其文件的URL地址创建即可,下面给出一个示例:
// 创建通知内容
let content = UNMutableNotificationContent()
content.title = "ios 10 local push test"
content.subtitle = "local push subtitle"
content.body = "这是一个iOS 10 之后的本地通知测试文本,这里显示的是消息的详细内容,另外这是一个添加的附件图片的通知"
content.sound = .default
content.userInfo = ["info": "这里的信息是传递给app的payload内容"]
// 加载本地的一张图片作为附件
if let url = Bundle.main.url(forResource: "111", withExtension: "png") {
if let attch = try? UNNotificationAttachment(identifier: "identifierAttachment", url: url, options: nil) {
content.attachments = [attch]
}
}
// 创建触发方式,10s后触发
let timer = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// 创建通知请求
let req = UNNotificationRequest(identifier: "reqid", content: content, trigger: timer)
// 添加请求到通知中心
UNUserNotificationCenter.current().add(req) {
(error) in
print(error)
print("prepare for local push")
}
也可以加载一段音乐,例如加载一首歌,只需要把上面的加载附件的部分代码修改为:
// 加载本地的一张图片作为附件
if let url = Bundle.main.url(forResource: "music", withExtension: "mp3") {
if let attch = try? UNNotificationAttachment(identifier: "identifierAttachment", url: url, options: nil) {
content.attachments = [attch]
}
}
可以看到,这里可以直接播放,视频的加载方式类似,只要不超过大小限制即可!
远程通知的附件数据是存放在服务端的,所以我们发送的Payload需要添加mutable-content字段,并设置其值为1 ,告诉系统此通知是可变的,然后再通过Service Extensions服务来下载对应的数据创建attachments,添加到相应的通知里面,显示在通知里。
Payload 模板:
{
"aps":
{
"alert":
{
"title":"iOS10远程推送标题",
"subtitle" : "iOS10 远程推送副标题",
"body":"这是在iOS10以上版本的推送内容,并且携带来一个图片附件"
},
"badge":1,
"mutable-content":1,
"sound":"default",
"image":"http://pic29.nipic.com/20130511/9252150_174018365301_2.jpg"
}
}
首先,添加一个扩展服务Target
选择:导航栏 File -> New -> Target
在弹出的页面中选择Notification Service Extension,下一步,起一个名称,完成即可!可以看到,项目中多了一个Target,以及几个相关的文件:
我需要在生成的NotificationService文件里处理我们发送的通知,添加相应的附件,可以看到NotificationService是继承自UNNotificationContentExtension的,并重写了他的两个方法,定义了两个属性:
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
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]"
contentHandler(bestAttemptContent)
}
}
override func 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.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
如果我们什么都不更改,使用上面的Payload模版发送一个远程通知试试,会发现我们设置的标题后面多了个**[modified]**,这是因为修改了标题。可见,通知调用了这个方法,但是,当我们在这个文件内添加打印log的print,或者断点,log信息并没有输出,断点也没有停,虽然这个方法确实是被系统调用了,这给我们的调试带来了一些不方便。
我们主要是在didReceive方法里添加相应的处理:下载附件的数据,保存到本地,然后根据本地的文件URL创建UNNotificationAttachment实例对象,添加到通知里,并回调给系统。
以携带一张图为例:
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
print(bestAttemptContent.userInfo)
// 1. 获取payload内容
// 此处的 userInfo 即我们发送的Payload内容
if let aps = bestAttemptContent.userInfo["aps"] as? [String: Any] {
// 2. 获取到payload内的图片地址
if let imagePath = aps["image"] as? String {
// bestAttemptContent.body = "\(bestAttemptContent.body) +imagePath \(imagePath)"
if let url = URL(string: imagePath) {
// bestAttemptContent.body = "\(bestAttemptContent.body) +url \(url)"
// 3. 根据URL地址获取图片数据
if let data = try? Data.init(contentsOf: url) {
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
// 4. 创建本地文件地址,最好在payload中添加一个文件名称,或者文件格式,在这里使用文件原名称/格式进行存储;这里直接写死
let fileUrl = URL.init(fileURLWithPath: path! + "/image.jpg")
// bestAttemptContent.body = "\(bestAttemptContent.body) +file \(fileUrl)"
// 5. 保存图片数据到本地
try? data.write(to: fileUrl)
// 6. 根据本地URL地址创建UNNotificationAttachment
if let att = try? UNNotificationAttachment(identifier: "imageattac", url: fileUrl, options: nil) {
bestAttemptContent.attachments = [att]
}
} /* if let data = end*/
} /* if let url = end*/
}/* if ler imagePath = end*/
}/* if let aps = end*/
// 7. 回调
contentHandler(bestAttemptContent)
}
实例代码中的1.–7.是相关需要操作的步骤,还有注释掉的bestAttemptContent.body = 部分代码,因为无法打印log,为了能看到相关的信息,我就把这些信息添加到body里面,然后显示在通知里了。
如果是其他的文件,例如视频、音频文件,都可以按此步骤来操作,下载的方式可以使用自己项目中使用的网络框架进行。需要注意的是,此处下载的时间只有30s,所以附件一定要控制大小,如果在此时间内没有下载完成,或者下载失败,将会以原通知的内容进行推送,不含有附件。
PS: 这里需要注意,使用到这个功能的时候,一定要有访问网络的权限,也就是要在弹出那个网络权限的选择框之后。我在写demo的时候,因为没有用到网络,在此之前没有申请网络的授权访问,所以一直没有出现图片!