[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments

通知相关系列文章
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")
        }

通知执行后,在锁屏状态通知中心显示为:
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第1张图片

弹框的显示效果
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第2张图片

弹框下拉后会显示一个大图
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第3张图片

也可以加载一段音乐,例如加载一首歌,只需要把上面的加载附件的部分代码修改为:

// 加载本地的一张图片作为附件
        if let url = Bundle.main.url(forResource: "music", withExtension: "mp3") {
     
            if let attch = try? UNNotificationAttachment(identifier: "identifierAttachment", url: url, options: nil) {
     
                content.attachments = [attch]
            }
        }

弹框下拉后如下图效果:
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第4张图片

可以看到,这里可以直接播放,视频的加载方式类似,只要不超过大小限制即可!

远程通知携带附加包

远程通知的附件数据是存放在服务端的,所以我们发送的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"
    }
}


添加 Service Extension 服务

首先,添加一个扩展服务Target
选择:导航栏 File -> New -> Target

[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第5张图片

在弹出的页面中选择Notification Service Extension,下一步,起一个名称,完成即可!可以看到,项目中多了一个Target,以及几个相关的文件:

[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第6张图片

我需要在生成的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里面,然后显示在通知里了。

这时,再去发送一个通知:
在锁屏页面,或者弹框,是这样的
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第7张图片

点击查看,或者下拉弹框,会显示大图
[iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments_第8张图片

如果是其他的文件,例如视频、音频文件,都可以按此步骤来操作,下载的方式可以使用自己项目中使用的网络框架进行。需要注意的是,此处下载的时间只有30s,所以附件一定要控制大小,如果在此时间内没有下载完成,或者下载失败,将会以原通知的内容进行推送,不含有附件。

PS: 这里需要注意,使用到这个功能的时候,一定要有访问网络的权限,也就是要在弹出那个网络权限的选择框之后。我在写demo的时候,因为没有用到网络,在此之前没有申请网络的授权访问,所以一直没有出现图片!

你可能感兴趣的:(Swift,远程通知,iOS10,通知,Media,Attachments,推送附加包,附加包)