美文网首页iOS 开发每天分享优质文章
[iOS] 通知详解: iOS 10 UserNotificat

[iOS] 通知详解: iOS 10 UserNotificat

作者: 流火绯瞳 | 来源:发表于2018-11-06 15:44 被阅读47次

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

    添加 Service Extension 服务

    首先,添加一个扩展服务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的时候,因为没有用到网络,在此之前没有申请网络的授权访问,所以一直没有出现图片!

    相关文章

      网友评论

        本文标题:[iOS] 通知详解: iOS 10 UserNotificat

        本文链接:https://www.haomeiwen.com/subject/dimotqtx.html